| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import { API, showError } from '../helpers'; |
| | import { |
| | prepareCredentialRequestOptions, |
| | buildAssertionResult, |
| | isPasskeySupported, |
| | } from '../helpers/passkey'; |
| |
|
| | |
| | |
| | |
| | |
| | export class SecureVerificationService { |
| | |
| | |
| | |
| | |
| | static async checkAvailableVerificationMethods() { |
| | try { |
| | const [twoFAResponse, passkeyResponse, passkeySupported] = |
| | await Promise.all([ |
| | API.get('/api/user/2fa/status'), |
| | API.get('/api/user/passkey'), |
| | isPasskeySupported(), |
| | ]); |
| |
|
| | console.log('=== DEBUGGING VERIFICATION METHODS ==='); |
| | console.log('2FA Response:', JSON.stringify(twoFAResponse, null, 2)); |
| | console.log( |
| | 'Passkey Response:', |
| | JSON.stringify(passkeyResponse, null, 2), |
| | ); |
| |
|
| | const has2FA = |
| | twoFAResponse.data?.success && |
| | twoFAResponse.data?.data?.enabled === true; |
| | const hasPasskey = |
| | passkeyResponse.data?.success && |
| | passkeyResponse.data?.data?.enabled === true; |
| |
|
| | console.log('has2FA calculation:', { |
| | success: twoFAResponse.data?.success, |
| | dataExists: !!twoFAResponse.data?.data, |
| | enabled: twoFAResponse.data?.data?.enabled, |
| | result: has2FA, |
| | }); |
| |
|
| | console.log('hasPasskey calculation:', { |
| | success: passkeyResponse.data?.success, |
| | dataExists: !!passkeyResponse.data?.data, |
| | enabled: passkeyResponse.data?.data?.enabled, |
| | result: hasPasskey, |
| | }); |
| |
|
| | const result = { |
| | has2FA, |
| | hasPasskey, |
| | passkeySupported, |
| | }; |
| |
|
| | return result; |
| | } catch (error) { |
| | console.error('Failed to check verification methods:', error); |
| | return { |
| | has2FA: false, |
| | hasPasskey: false, |
| | passkeySupported: false, |
| | }; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | static async verify2FA(code) { |
| | if (!code?.trim()) { |
| | throw new Error('请输入验证码或备用码'); |
| | } |
| |
|
| | |
| | const verifyResponse = await API.post('/api/verify', { |
| | method: '2fa', |
| | code: code.trim(), |
| | }); |
| |
|
| | if (!verifyResponse.data?.success) { |
| | throw new Error(verifyResponse.data?.message || '验证失败'); |
| | } |
| |
|
| | |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | static async verifyPasskey() { |
| | try { |
| | |
| | const beginResponse = await API.post('/api/user/passkey/verify/begin'); |
| | if (!beginResponse.data?.success) { |
| | throw new Error(beginResponse.data?.message || '开始验证失败'); |
| | } |
| |
|
| | |
| | const publicKey = prepareCredentialRequestOptions( |
| | beginResponse.data.data.options, |
| | ); |
| |
|
| | |
| | const credential = await navigator.credentials.get({ publicKey }); |
| | if (!credential) { |
| | throw new Error('Passkey 验证被取消'); |
| | } |
| |
|
| | |
| | const assertionResult = buildAssertionResult(credential); |
| |
|
| | |
| | const finishResponse = await API.post( |
| | '/api/user/passkey/verify/finish', |
| | assertionResult, |
| | ); |
| | if (!finishResponse.data?.success) { |
| | throw new Error(finishResponse.data?.message || '验证失败'); |
| | } |
| |
|
| | |
| | const verifyResponse = await API.post('/api/verify', { |
| | method: 'passkey', |
| | }); |
| |
|
| | if (!verifyResponse.data?.success) { |
| | throw new Error(verifyResponse.data?.message || '验证失败'); |
| | } |
| |
|
| | |
| | } catch (error) { |
| | if (error.name === 'NotAllowedError') { |
| | throw new Error('Passkey 验证被取消或超时'); |
| | } else if (error.name === 'InvalidStateError') { |
| | throw new Error('Passkey 验证状态无效'); |
| | } else { |
| | throw error; |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | static async verify(method, code = '') { |
| | switch (method) { |
| | case '2fa': |
| | return await this.verify2FA(code); |
| | case 'passkey': |
| | return await this.verifyPasskey(); |
| | default: |
| | throw new Error(`不支持的验证方式: ${method}`); |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | export const createApiCalls = { |
| | |
| | |
| | |
| | |
| | viewChannelKey: (channelId) => async () => { |
| | |
| | const response = await API.post(`/api/channel/${channelId}/key`, {}); |
| | return response.data; |
| | }, |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | custom: |
| | (url, method = 'POST', extraData = {}) => |
| | async () => { |
| | |
| | const data = extraData; |
| |
|
| | let response; |
| | switch (method.toUpperCase()) { |
| | case 'GET': |
| | response = await API.get(url, { params: data }); |
| | break; |
| | case 'POST': |
| | response = await API.post(url, data); |
| | break; |
| | case 'PUT': |
| | response = await API.put(url, data); |
| | break; |
| | case 'DELETE': |
| | response = await API.delete(url, { data }); |
| | break; |
| | default: |
| | throw new Error(`不支持的HTTP方法: ${method}`); |
| | } |
| | return response.data; |
| | }, |
| | }; |
| |
|