3
3
import { Controller } from '@hotwired/stimulus' ;
4
4
import {
5
5
AuthenticationResponseJSON ,
6
- RegistrationResponseJSON
6
+ RegistrationResponseJSON ,
7
+ PublicKeyCredentialRequestOptionsJSON ,
8
+ PublicKeyCredentialCreationOptionsJSON
7
9
} from '@simplewebauthn/types' ;
8
- import { browserSupportsWebAuthn , browserSupportsWebAuthnAutofill , startAuthentication , startRegistration } from '@simplewebauthn/browser' ;
10
+ import { browserSupportsWebAuthn , browserSupportsWebAuthnAutofill , startAuthentication , startRegistration , base64URLStringToBuffer , bufferToBase64URLString } from '@simplewebauthn/browser' ;
9
11
10
12
export default class extends Controller {
11
13
static values = {
@@ -89,7 +91,11 @@ export default class extends Controller {
89
91
private async _processSignin ( optionsResponseJson : Object , useBrowserAutofill : boolean ) : Promise < void > {
90
92
try {
91
93
// @ts -ignore
92
- const authenticatorResponse = await startAuthentication ( { optionsJSON : optionsResponseJson , useBrowserAutofill } ) ;
94
+ optionsResponseJson = this . _processExtensionsInput ( optionsResponseJson ) ;
95
+ // @ts -ignore
96
+ let authenticatorResponse = await startAuthentication ( { optionsJSON : optionsResponseJson , useBrowserAutofill } ) ;
97
+ // @ts -ignore
98
+ authenticatorResponse = this . _processExtensionsOutput ( authenticatorResponse ) ;
93
99
this . _dispatchEvent ( 'webauthn:authenticator:response' , { response : authenticatorResponse } ) ;
94
100
if ( this . requestResultFieldValue && this . element instanceof HTMLFormElement ) {
95
101
this . element . querySelector ( this . requestResultFieldValue ) ?. setAttribute ( 'value' , JSON . stringify ( authenticatorResponse ) ) ;
@@ -114,13 +120,16 @@ export default class extends Controller {
114
120
return ;
115
121
}
116
122
event . preventDefault ( ) ;
117
- const optionsResponseJson = await this . _getPublicKeyCredentialCreationOptions ( null ) ;
123
+ let optionsResponseJson = await this . _getPublicKeyCredentialCreationOptions ( null ) ;
118
124
if ( ! optionsResponseJson ) {
119
125
return ;
120
126
}
121
127
128
+ optionsResponseJson = this . _processExtensionsInput ( optionsResponseJson ) ;
129
+ // @ts -ignore
130
+ let authenticatorResponse = await startRegistration ( { optionsJSON : optionsResponseJson } ) ;
122
131
// @ts -ignore
123
- const authenticatorResponse = await startRegistration ( { optionsJSON : optionsResponseJson } ) ;
132
+ authenticatorResponse = this . _processExtensionsOutput ( authenticatorResponse ) ;
124
133
this . _dispatchEvent ( 'webauthn:authenticator:response' , { response : authenticatorResponse } ) ;
125
134
if ( this . creationResultFieldValue && this . element instanceof HTMLFormElement ) {
126
135
this . element . querySelector ( this . creationResultFieldValue ) ?. setAttribute ( 'value' , JSON . stringify ( authenticatorResponse ) ) ;
@@ -228,4 +237,89 @@ export default class extends Controller {
228
237
229
238
return attestationResponseJSON ;
230
239
}
240
+
241
+ private _processExtensionsInput ( options : Object | PublicKeyCredentialRequestOptionsJSON | PublicKeyCredentialCreationOptionsJSON ) : Object | PublicKeyCredentialRequestOptionsJSON | PublicKeyCredentialCreationOptionsJSON {
242
+ // @ts -ignore
243
+ if ( ! options || ! options . extensions ) {
244
+ return options ;
245
+ }
246
+
247
+ // @ts -ignore
248
+ if ( options . extensions . prf ) {
249
+ // @ts -ignore
250
+ options . extensions . prf = this . _processPrfInput ( options . extensions . prf ) ;
251
+ }
252
+
253
+ return options ;
254
+ }
255
+
256
+ private _processPrfInput ( prf : Object ) : Object {
257
+ // @ts -ignore
258
+ if ( prf . eval ) {
259
+ // @ts -ignore
260
+ prf . eval = this . _importPrfValues ( eval ) ;
261
+ }
262
+
263
+ // @ts -ignore
264
+ if ( prf . evalByCredential ) {
265
+ // @ts -ignore
266
+ Object . keys ( prf . evalByCredential ) . forEach ( ( key ) => {
267
+ // @ts -ignore
268
+ prf . evalByCredential [ key ] = this . _importPrfValues ( prf . evalByCredential [ key ] ) ;
269
+ } ) ;
270
+ }
271
+
272
+ return prf ;
273
+ }
274
+
275
+ private _importPrfValues ( values : Object ) : Object {
276
+ // @ts -ignore
277
+ values . first = base64URLStringToBuffer ( values . first ) ;
278
+ // @ts -ignore
279
+ if ( values . second ) {
280
+ // @ts -ignore
281
+ values . second = base64URLStringToBuffer ( values . second ) ;
282
+ }
283
+
284
+ return values ;
285
+ }
286
+
287
+ private _processExtensionsOutput ( options : Object | AuthenticationResponseJSON | RegistrationResponseJSON ) : Object | PublicKeyCredentialRequestOptionsJSON | PublicKeyCredentialCreationOptionsJSON {
288
+ // @ts -ignore
289
+ if ( ! options || ! options . extensions ) {
290
+ return options ;
291
+ }
292
+
293
+ // @ts -ignore
294
+ if ( options . extensions . prf ) {
295
+ // @ts -ignore
296
+ options . extensions . prf = this . _processPrfOutput ( options . extensions . prf ) ;
297
+ }
298
+
299
+ return options ;
300
+ }
301
+
302
+ private _processPrfOutput ( prf : Object ) : Object {
303
+ // @ts -ignore
304
+ if ( ! prf . result ) {
305
+ return prf
306
+ }
307
+
308
+ // @ts -ignore
309
+ prf . result = this . _exportPrfValues ( prf . result ) ;
310
+
311
+ return prf ;
312
+ }
313
+
314
+ private _exportPrfValues ( values : Object ) : Object {
315
+ // @ts -ignore
316
+ values . first = bufferToBase64URLString ( values . first ) ;
317
+ // @ts -ignore
318
+ if ( values . second ) {
319
+ // @ts -ignore
320
+ values . second = bufferToBase64URLString ( values . second ) ;
321
+ }
322
+
323
+ return values ;
324
+ }
231
325
}
0 commit comments