1- import { FC , useCallback , useEffect , useState } from "react" ;
1+ import { FC , useCallback , useEffect , useRef , useState } from "react" ;
22import useInterval from "@hooks/useInterval" ;
33import useTimeout from "@hooks/useTimeout" ;
44import useKeyDown from "@hooks/useKeyDown" ;
@@ -96,65 +96,76 @@ const U2fDialog: FC = () => {
9696 if ( states . reject ) states . reject ( "user canceled" ) ;
9797 } ;
9898
99+ const refPasskey = useRef < null | AbortController > ( null ) ;
100+
99101 const onSubmit = async ( method : string = tabValue ) => {
100102 if ( tokenAvailable ) {
101103 const states = useU2fDialog . getState ( ) ;
102104 if ( states . resolve ) states . resolve ( u2fToken ! ) ;
103105 states . closeDialog ( ) ;
104106 return ;
105107 }
106- let data : any ;
107- switch ( method ) {
108- case "phone" :
109- if ( smsCode === "" ) {
110- toast . error ( "验证码不能为空" ) ;
111- return ;
112- }
113- if ( smsCode . length != 5 ) {
114- toast . error ( "短信验证码有误" ) ;
115- return ;
116- }
117- data = { code : smsCode } ;
118- break ;
119- case "mfa" :
120- if ( mfaCode === "" ) {
121- toast . error ( "校验码不能为空" ) ;
122- return ;
123- }
124- if ( mfaCode . length != 6 ) {
125- toast . error ( "校验码有误" ) ;
126- return ;
127- }
128- data = { code : mfaCode } ;
129- break ;
130- case "passkey" :
131- const {
132- data : { data : options } ,
133- } = await apiV1User . get ( "passkey/options" ) ;
134- options . publicKey . challenge = coerceToArrayBuffer (
135- options . publicKey . challenge ,
136- ) ;
137- options . publicKey . allowCredentials =
138- options . publicKey . allowCredentials . map ( ( cred : any ) => {
139- cred . id = coerceToArrayBuffer ( cred . id ) ;
140- return cred ;
141- } ) ;
142- const credential = await navigator . credentials . get ( options ) ;
143- if ( credential ?. type !== "public-key" ) {
144- toast . error ( `获取凭据失败,凭据类型不正确: ${ credential ?. type } ` ) ;
145- return ;
146- }
147- const pubKeyCred = credential as any ;
148- data = {
149- id : pubKeyCred . id ,
150- rawId : coerceToBase64Url ( pubKeyCred . rawId ) ,
151- response : coerceResponseToBase64Url ( pubKeyCred . response ) ,
152- type : pubKeyCred . type ,
153- } ;
154- break ;
155- }
156- setIsLoading ( true ) ;
157108 try {
109+ setIsLoading ( true ) ;
110+ let data : any ;
111+ switch ( method ) {
112+ case "phone" :
113+ if ( smsCode === "" ) {
114+ toast . error ( "验证码不能为空" ) ;
115+ break ;
116+ }
117+ if ( smsCode . length != 5 ) {
118+ toast . error ( "短信验证码有误" ) ;
119+ break ;
120+ }
121+ data = { code : smsCode } ;
122+ break ;
123+ case "mfa" :
124+ if ( mfaCode === "" ) {
125+ toast . error ( "校验码不能为空" ) ;
126+ break ;
127+ }
128+ if ( mfaCode . length != 6 ) {
129+ toast . error ( "校验码有误" ) ;
130+ break ;
131+ }
132+ data = { code : mfaCode } ;
133+ break ;
134+ case "passkey" :
135+ if ( refPasskey . current ) refPasskey . current . abort ( ) ;
136+ const controller = new AbortController ( ) ;
137+ refPasskey . current = controller ;
138+ const {
139+ data : { data : options } ,
140+ } = await apiV1User . get ( "passkey/options" , {
141+ signal : controller . signal ,
142+ } ) ;
143+ options . publicKey . challenge = coerceToArrayBuffer (
144+ options . publicKey . challenge ,
145+ ) ;
146+ options . publicKey . allowCredentials =
147+ options . publicKey . allowCredentials . map ( ( cred : any ) => {
148+ cred . id = coerceToArrayBuffer ( cred . id ) ;
149+ return cred ;
150+ } ) ;
151+ const credential = await navigator . credentials . get ( {
152+ ...options ,
153+ signal : controller . signal ,
154+ } ) ;
155+ if ( credential ?. type !== "public-key" ) {
156+ toast . error ( `获取凭据失败,凭据类型不正确: ${ credential ?. type } ` ) ;
157+ break ;
158+ }
159+ const pubKeyCred = credential as any ;
160+ data = {
161+ id : pubKeyCred . id ,
162+ rawId : coerceToBase64Url ( pubKeyCred . rawId ) ,
163+ response : coerceResponseToBase64Url ( pubKeyCred . response ) ,
164+ type : pubKeyCred . type ,
165+ } ;
166+ break ;
167+ }
168+ if ( ! data ) return ;
158169 const {
159170 data : { data : result } ,
160171 } = await apiV1User . post < { data : User . U2F . Result } > (
@@ -166,7 +177,15 @@ const U2fDialog: FC = () => {
166177 if ( states . resolve ) states . resolve ( result ) ;
167178 states . closeDialog ( ) ;
168179 } catch ( err ) {
169- if ( err instanceof Error ) toast . error ( err . message ) ;
180+ if ( err instanceof Error ) {
181+ switch ( err . name ) {
182+ case "AbortError" :
183+ console . log ( "U2F bypass error" , err ) ;
184+ break ;
185+ default :
186+ toast . error ( err . message ) ;
187+ }
188+ }
170189 }
171190 setIsLoading ( false ) ;
172191 } ;
0 commit comments