1
- import { fromByteArray } from "base64-js" ;
2
1
import "formdata-polyfill" ;
3
2
import $ from "jquery" ;
4
3
import "weakmap-polyfill" ;
4
+ import "webauthn-polyfills" ;
5
5
6
6
import {
7
7
type AuthenticatorValidationChallenge ,
@@ -257,47 +257,9 @@ class AutosubmitStage extends Stage<AutosubmitChallenge> {
257
257
}
258
258
}
259
259
260
- export interface Assertion {
261
- id : string ;
262
- rawId : string ;
263
- type : string ;
264
- registrationClientExtensions : string ;
265
- response : {
266
- clientDataJSON : string ;
267
- attestationObject : string ;
268
- } ;
269
- }
270
-
271
- export interface AuthAssertion {
272
- id : string ;
273
- rawId : string ;
274
- type : string ;
275
- assertionClientExtensions : string ;
276
- response : {
277
- clientDataJSON : string ;
278
- authenticatorData : string ;
279
- signature : string ;
280
- userHandle : string | null ;
281
- } ;
282
- }
283
-
284
260
class AuthenticatorValidateStage extends Stage < AuthenticatorValidationChallenge > {
285
261
deviceChallenge ?: DeviceChallenge ;
286
262
287
- b64enc ( buf : Uint8Array ) : string {
288
- return fromByteArray ( buf ) . replace ( / \+ / g, "-" ) . replace ( / \/ / g, "_" ) . replace ( / = / g, "" ) ;
289
- }
290
-
291
- b64RawEnc ( buf : Uint8Array ) : string {
292
- return fromByteArray ( buf ) . replace ( / \+ / g, "-" ) . replace ( / \/ / g, "_" ) ;
293
- }
294
-
295
- u8arr ( input : string ) : Uint8Array {
296
- return Uint8Array . from ( atob ( input . replace ( / _ / g, "/" ) . replace ( / - / g, "+" ) ) , ( c ) =>
297
- c . charCodeAt ( 0 ) ,
298
- ) ;
299
- }
300
-
301
263
checkWebAuthnSupport ( ) : boolean {
302
264
if ( "credentials" in navigator ) {
303
265
return true ;
@@ -310,98 +272,6 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
310
272
return false ;
311
273
}
312
274
313
- /**
314
- * Transforms items in the credentialCreateOptions generated on the server
315
- * into byte arrays expected by the navigator.credentials.create() call
316
- */
317
- transformCredentialCreateOptions (
318
- credentialCreateOptions : PublicKeyCredentialCreationOptions ,
319
- userId : string ,
320
- ) : PublicKeyCredentialCreationOptions {
321
- const user = credentialCreateOptions . user ;
322
- // Because json can't contain raw bytes, the server base64-encodes the User ID
323
- // So to get the base64 encoded byte array, we first need to convert it to a regular
324
- // string, then a byte array, re-encode it and wrap that in an array.
325
- const stringId = decodeURIComponent ( window . atob ( userId ) ) ;
326
- user . id = this . u8arr ( this . b64enc ( this . u8arr ( stringId ) ) ) ;
327
- const challenge = this . u8arr ( credentialCreateOptions . challenge . toString ( ) ) ;
328
-
329
- return Object . assign ( { } , credentialCreateOptions , {
330
- challenge,
331
- user,
332
- } ) ;
333
- }
334
-
335
- /**
336
- * Transforms the binary data in the credential into base64 strings
337
- * for posting to the server.
338
- * @param {PublicKeyCredential } newAssertion
339
- */
340
- transformNewAssertionForServer ( newAssertion : PublicKeyCredential ) : Assertion {
341
- const attObj = new Uint8Array (
342
- ( newAssertion . response as AuthenticatorAttestationResponse ) . attestationObject ,
343
- ) ;
344
- const clientDataJSON = new Uint8Array ( newAssertion . response . clientDataJSON ) ;
345
- const rawId = new Uint8Array ( newAssertion . rawId ) ;
346
-
347
- const registrationClientExtensions = newAssertion . getClientExtensionResults ( ) ;
348
- return {
349
- id : newAssertion . id ,
350
- rawId : this . b64enc ( rawId ) ,
351
- type : newAssertion . type ,
352
- registrationClientExtensions : JSON . stringify ( registrationClientExtensions ) ,
353
- response : {
354
- clientDataJSON : this . b64enc ( clientDataJSON ) ,
355
- attestationObject : this . b64enc ( attObj ) ,
356
- } ,
357
- } ;
358
- }
359
-
360
- transformCredentialRequestOptions (
361
- credentialRequestOptions : PublicKeyCredentialRequestOptions ,
362
- ) : PublicKeyCredentialRequestOptions {
363
- const challenge = this . u8arr ( credentialRequestOptions . challenge . toString ( ) ) ;
364
-
365
- const allowCredentials = ( credentialRequestOptions . allowCredentials || [ ] ) . map (
366
- ( credentialDescriptor ) => {
367
- const id = this . u8arr ( credentialDescriptor . id . toString ( ) ) ;
368
- return Object . assign ( { } , credentialDescriptor , { id } ) ;
369
- } ,
370
- ) ;
371
-
372
- return Object . assign ( { } , credentialRequestOptions , {
373
- challenge,
374
- allowCredentials,
375
- } ) ;
376
- }
377
-
378
- /**
379
- * Encodes the binary data in the assertion into strings for posting to the server.
380
- * @param {PublicKeyCredential } newAssertion
381
- */
382
- transformAssertionForServer ( newAssertion : PublicKeyCredential ) : AuthAssertion {
383
- const response = newAssertion . response as AuthenticatorAssertionResponse ;
384
- const authData = new Uint8Array ( response . authenticatorData ) ;
385
- const clientDataJSON = new Uint8Array ( response . clientDataJSON ) ;
386
- const rawId = new Uint8Array ( newAssertion . rawId ) ;
387
- const sig = new Uint8Array ( response . signature ) ;
388
- const assertionClientExtensions = newAssertion . getClientExtensionResults ( ) ;
389
-
390
- return {
391
- id : newAssertion . id ,
392
- rawId : this . b64enc ( rawId ) ,
393
- type : newAssertion . type ,
394
- assertionClientExtensions : JSON . stringify ( assertionClientExtensions ) ,
395
-
396
- response : {
397
- clientDataJSON : this . b64RawEnc ( clientDataJSON ) ,
398
- signature : this . b64RawEnc ( sig ) ,
399
- authenticatorData : this . b64RawEnc ( authData ) ,
400
- userHandle : null ,
401
- } ,
402
- } ;
403
- }
404
-
405
275
render ( ) {
406
276
if ( this . challenge . deviceChallenges . length === 1 ) {
407
277
this . deviceChallenge = this . challenge . deviceChallenges [ 0 ] ;
@@ -505,24 +375,18 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
505
375
` ) ;
506
376
navigator . credentials
507
377
. get ( {
508
- publicKey : this . transformCredentialRequestOptions (
509
- this . deviceChallenge ?. challenge as PublicKeyCredentialRequestOptions ,
378
+ publicKey : PublicKeyCredential . parseRequestOptionsFromJSON (
379
+ this . deviceChallenge ?. challenge as PublicKeyCredentialRequestOptionsJSON ,
510
380
) ,
511
381
} )
512
382
. then ( ( assertion ) => {
513
383
if ( ! assertion ) {
514
384
throw new Error ( "No assertion" ) ;
515
385
}
516
386
try {
517
- // we now have an authentication assertion! encode the byte arrays contained
518
- // in the assertion data as strings for posting to the server
519
- const transformedAssertionForServer = this . transformAssertionForServer (
520
- assertion as PublicKeyCredential ,
521
- ) ;
522
-
523
387
// post the assertion to the server for verification.
524
388
this . executor . submit ( {
525
- webauthn : transformedAssertionForServer ,
389
+ webauthn : ( assertion as PublicKeyCredential ) . toJSON ( ) ,
526
390
} ) ;
527
391
} catch ( err ) {
528
392
throw new Error ( `Error when validating assertion on server: ${ err } ` ) ;
0 commit comments