useSecureVerification.jsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /*
  2. Copyright (C) 2025 QuantumNous
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <https://www.gnu.org/licenses/>.
  13. For commercial licensing, please contact support@quantumnous.com
  14. */
  15. import { useState, useEffect, useCallback } from 'react';
  16. import { useTranslation } from 'react-i18next';
  17. import { SecureVerificationService } from '../../services/secureVerification';
  18. import { showError, showSuccess } from '../../helpers';
  19. /**
  20. * 通用安全验证 Hook
  21. * @param {Object} options - 配置选项
  22. * @param {Function} options.onSuccess - 验证成功回调
  23. * @param {Function} options.onError - 验证失败回调
  24. * @param {string} options.successMessage - 成功提示消息
  25. * @param {boolean} options.autoReset - 验证完成后是否自动重置状态,默认为 true
  26. */
  27. export const useSecureVerification = ({
  28. onSuccess,
  29. onError,
  30. successMessage,
  31. autoReset = true
  32. } = {}) => {
  33. const { t } = useTranslation();
  34. // 验证方式可用性状态
  35. const [verificationMethods, setVerificationMethods] = useState({
  36. has2FA: false,
  37. hasPasskey: false,
  38. passkeySupported: false
  39. });
  40. // 模态框状态
  41. const [isModalVisible, setIsModalVisible] = useState(false);
  42. // 当前验证状态
  43. const [verificationState, setVerificationState] = useState({
  44. method: null, // '2fa' | 'passkey'
  45. loading: false,
  46. code: '',
  47. apiCall: null
  48. });
  49. // 检查可用的验证方式
  50. const checkVerificationMethods = useCallback(async () => {
  51. const methods = await SecureVerificationService.checkAvailableVerificationMethods();
  52. setVerificationMethods(methods);
  53. return methods;
  54. }, []);
  55. // 初始化时检查验证方式
  56. useEffect(() => {
  57. checkVerificationMethods();
  58. }, [checkVerificationMethods]);
  59. // 重置状态
  60. const resetState = useCallback(() => {
  61. setVerificationState({
  62. method: null,
  63. loading: false,
  64. code: '',
  65. apiCall: null
  66. });
  67. setIsModalVisible(false);
  68. }, []);
  69. // 开始验证流程
  70. const startVerification = useCallback(async (apiCall, options = {}) => {
  71. const { preferredMethod, title, description } = options;
  72. // 检查验证方式
  73. const methods = await checkVerificationMethods();
  74. if (!methods.has2FA && !methods.hasPasskey) {
  75. const errorMessage = t('您需要先启用两步验证或 Passkey 才能执行此操作');
  76. showError(errorMessage);
  77. onError?.(new Error(errorMessage));
  78. return false;
  79. }
  80. // 设置默认验证方式
  81. let defaultMethod = preferredMethod;
  82. if (!defaultMethod) {
  83. if (methods.hasPasskey && methods.passkeySupported) {
  84. defaultMethod = 'passkey';
  85. } else if (methods.has2FA) {
  86. defaultMethod = '2fa';
  87. }
  88. }
  89. setVerificationState(prev => ({
  90. ...prev,
  91. method: defaultMethod,
  92. apiCall,
  93. title,
  94. description
  95. }));
  96. setIsModalVisible(true);
  97. return true;
  98. }, [checkVerificationMethods, onError, t]);
  99. // 执行验证
  100. const executeVerification = useCallback(async (method, code = '') => {
  101. if (!verificationState.apiCall) {
  102. showError(t('验证配置错误'));
  103. return;
  104. }
  105. setVerificationState(prev => ({ ...prev, loading: true }));
  106. try {
  107. const result = await SecureVerificationService.verify(method, {
  108. code,
  109. apiCall: verificationState.apiCall
  110. });
  111. // 显示成功消息
  112. if (successMessage) {
  113. showSuccess(successMessage);
  114. }
  115. // 调用成功回调
  116. onSuccess?.(result, method);
  117. // 自动重置状态
  118. if (autoReset) {
  119. resetState();
  120. }
  121. return result;
  122. } catch (error) {
  123. showError(error.message || t('验证失败,请重试'));
  124. onError?.(error);
  125. throw error;
  126. } finally {
  127. setVerificationState(prev => ({ ...prev, loading: false }));
  128. }
  129. }, [verificationState.apiCall, successMessage, onSuccess, onError, autoReset, resetState, t]);
  130. // 设置验证码
  131. const setVerificationCode = useCallback((code) => {
  132. setVerificationState(prev => ({ ...prev, code }));
  133. }, []);
  134. // 切换验证方式
  135. const switchVerificationMethod = useCallback((method) => {
  136. setVerificationState(prev => ({ ...prev, method, code: '' }));
  137. }, []);
  138. // 取消验证
  139. const cancelVerification = useCallback(() => {
  140. resetState();
  141. }, [resetState]);
  142. // 检查是否可以使用某种验证方式
  143. const canUseMethod = useCallback((method) => {
  144. switch (method) {
  145. case '2fa':
  146. return verificationMethods.has2FA;
  147. case 'passkey':
  148. return verificationMethods.hasPasskey && verificationMethods.passkeySupported;
  149. default:
  150. return false;
  151. }
  152. }, [verificationMethods]);
  153. // 获取推荐的验证方式
  154. const getRecommendedMethod = useCallback(() => {
  155. if (verificationMethods.hasPasskey && verificationMethods.passkeySupported) {
  156. return 'passkey';
  157. }
  158. if (verificationMethods.has2FA) {
  159. return '2fa';
  160. }
  161. return null;
  162. }, [verificationMethods]);
  163. return {
  164. // 状态
  165. isModalVisible,
  166. verificationMethods,
  167. verificationState,
  168. // 方法
  169. startVerification,
  170. executeVerification,
  171. cancelVerification,
  172. resetState,
  173. setVerificationCode,
  174. switchVerificationMethod,
  175. checkVerificationMethods,
  176. // 辅助方法
  177. canUseMethod,
  178. getRecommendedMethod,
  179. // 便捷属性
  180. hasAnyVerificationMethod: verificationMethods.has2FA || verificationMethods.hasPasskey,
  181. isLoading: verificationState.loading,
  182. currentMethod: verificationState.method,
  183. code: verificationState.code
  184. };
  185. };