| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | 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(),
|
| | ]);
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | 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;
|
| | },
|
| | };
|
| |
|