@@ -141,15 +141,40 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
141141 return false ;
142142 }
143143
144+ private pending = new Map < string , ( value : any ) => void > ( ) ;
145+
146+ private waitForResponse ( id : string ) : Promise < any > {
147+ return new Promise ( ( resolve ) => {
148+ this . pending . set ( id , resolve ) ;
149+ } ) ;
150+ }
151+
152+ private resolveResponse ( id : string , data : any ) {
153+ const resolve = this . pending . get ( id ) ;
154+ if ( resolve ) {
155+ resolve ( data ) ;
156+ this . pending . delete ( id ) ;
157+ }
158+ }
159+
160+ public async verifyAuto ( adminUser : AdminUser ) {
161+ const sessionId = crypto . randomUUID ( ) ;
162+ const jwt = this . adminforth . auth . issueJWT ( { sessionId, adminUserPk : adminUser . pk } , 'auto2FA' , '5m' ) ;
163+ this . adminforth . websocket . publish ( `/user2fa/${ adminUser . pk } ` , { sessionId : jwt } ) ;
164+ const result = await this . waitForResponse ( jwt ) ;
165+ this . adminforth . websocket . publish ( `/user2fa/${ adminUser . pk } -resolve` , { sessionId : jwt } ) ;
166+ return result ;
167+ }
168+
144169 public async verify (
145170 confirmationResult : Record < string , any > ,
146171 opts ?: { adminUser ?: AdminUser ; userPk ?: string ; cookies ?: any , response ?: IAdminForthHttpResponse , extra ?: HttpExtra }
147172 ) : Promise < { ok : true } | { error : string } > {
148173 if ( ! confirmationResult ) return { error : "Confirmation result is required" } ;
149174 if ( ! opts . adminUser ) return { error : "Admin user is required" } ;
150175 if ( ! opts . userPk ) return { error : "User PK is required" } ;
151- const cookies = opts . extra . cookies || opts . cookies ;
152- const response = opts . extra . response || opts . response ;
176+ const cookies = opts . extra ? .cookies || opts . cookies ;
177+ const response = opts . extra ? .response || opts . response ;
153178 if ( this . options . usersFilterToApply ) {
154179 const res = this . options . usersFilterToApply ( opts . adminUser ) ;
155180 if ( res === false ) {
@@ -753,7 +778,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
753778 method : 'POST' ,
754779 path : `/plugin/passkeys/registerPasskeyRequest` ,
755780 noAuth : false ,
756- handler : async ( { body, adminUser, response, cookies, extra } ) => {
781+ handler : async ( { body, adminUser, response, cookies, headers } ) => {
757782 const mode = body ?. mode ;
758783
759784 const confirmationResult = body ?. confirmationResult ;
@@ -762,7 +787,9 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
762787 userPk : adminUser . pk ,
763788 cookies : cookies ,
764789 response : response ,
765- extra : { ...extra }
790+ extra : {
791+ headers,
792+ } as HttpExtra
766793 } ) ;
767794 if ( ! verificationResult || ! ( 'ok' in verificationResult ) ) {
768795 return { ok : false , error : 'error' in verificationResult ? verificationResult . error : 'Verification failed' } ;
@@ -1049,5 +1076,55 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
10491076 }
10501077 }
10511078 } ) ;
1079+ server . endpoint ( {
1080+ method : 'POST' ,
1081+ path : `/plugin/passkeys/resolveVerifyAuto` ,
1082+ noAuth : false ,
1083+ handler : async ( { body, adminUser, response, cookies, headers } ) => {
1084+ const sessionsIds = body ?. sessionsIds ;
1085+ const confirmationResult = body ?. confirmationResult ;
1086+ const idsToResolve = sessionsIds ;
1087+
1088+ const resolveAllIdsAsFailed = ( message ) => {
1089+ for ( const id of idsToResolve ) {
1090+ this . resolveResponse ( id , { ok : false , error : message } ) ;
1091+ }
1092+ return { ok : false , error : message } ;
1093+ }
1094+
1095+ if ( ! ( sessionsIds ) || ! confirmationResult ) {
1096+ return ( resolveAllIdsAsFailed ( 'Confirmation window was closed or did not return required data' ) ) ;
1097+ }
1098+
1099+ for ( const id of idsToResolve ) {
1100+ const validationResult = await this . adminforth . auth . verify ( id , 'auto2FA' , false ) ;
1101+ if ( ! validationResult ) {
1102+ return ( resolveAllIdsAsFailed ( 'Invalid session ID or confirmation result' ) ) ;
1103+ }
1104+ if ( validationResult . adminUserPk !== adminUser . pk ) {
1105+ return ( resolveAllIdsAsFailed ( 'Session does not belong to the authenticated user' ) ) ;
1106+ }
1107+ }
1108+
1109+ const verificationResult = await this . verify ( confirmationResult , {
1110+ adminUser : adminUser ,
1111+ userPk : adminUser . pk ,
1112+ cookies : cookies ,
1113+ response : response ,
1114+ extra : {
1115+ headers : headers ,
1116+ } as HttpExtra
1117+ } ) ;
1118+ if ( ! verificationResult || ! ( 'ok' in verificationResult ) ) {
1119+ return ( resolveAllIdsAsFailed ( 'Verification failed' ) ) ;
1120+ }
1121+ if ( 'ok' in verificationResult && verificationResult . ok ) {
1122+ for ( const id of idsToResolve ) {
1123+ this . resolveResponse ( id , { ok : true , passkeyConfirmed : verificationResult } ) ;
1124+ }
1125+ return { ok : true } ;
1126+ }
1127+ }
1128+ } ) ;
10521129 }
10531130}
0 commit comments