1
1
import { isValidBrowser } from '@clerk/shared/browser' ;
2
2
import { ClerkRuntimeError } from '@clerk/shared/error' ;
3
3
import type {
4
+ __experimental_PublicKeyCredentialCreationOptionsWithoutExtensions ,
5
+ __experimental_PublicKeyCredentialRequestOptionsWithoutExtensions ,
6
+ __experimental_PublicKeyCredentialWithAuthenticatorAssertionResponse ,
7
+ __experimental_PublicKeyCredentialWithAuthenticatorAttestationResponse ,
4
8
PublicKeyCredentialCreationOptionsJSON ,
5
- PublicKeyCredentialCreationOptionsWithoutExtensions ,
9
+ PublicKeyCredentialRequestOptionsJSON ,
6
10
} from '@clerk/types' ;
7
11
8
- type PublicKeyCredentialWithAuthenticatorAttestationResponse = Omit <
9
- PublicKeyCredential ,
10
- 'response' | 'getClientExtensionResults'
11
- > & {
12
- response : Omit < AuthenticatorAttestationResponse , 'getAuthenticatorData' | 'getPublicKey' | 'getPublicKeyAlgorithm' > ;
13
- } ;
14
-
15
- type WebAuthnCreateCredentialReturn =
12
+ type CredentialReturn < T > =
16
13
| {
17
- publicKeyCredential : PublicKeyCredentialWithAuthenticatorAttestationResponse ;
14
+ publicKeyCredential : T ;
18
15
error : null ;
19
16
}
20
17
| {
21
18
publicKeyCredential : null ;
22
19
error : ClerkWebAuthnError | Error ;
23
20
} ;
24
21
25
- type ClerkWebAuthnErrorCode = 'passkey_exists' | 'passkey_registration_cancelled' | 'passkey_credential_failed' ;
22
+ type WebAuthnCreateCredentialReturn =
23
+ CredentialReturn < __experimental_PublicKeyCredentialWithAuthenticatorAttestationResponse > ;
24
+ type WebAuthnGetCredentialReturn =
25
+ CredentialReturn < __experimental_PublicKeyCredentialWithAuthenticatorAssertionResponse > ;
26
+
27
+ type ClerkWebAuthnErrorCode =
28
+ | 'passkey_exists'
29
+ | 'passkey_registration_cancelled'
30
+ | 'passkey_credential_create_failed'
31
+ | 'passkey_credential_get_failed' ;
26
32
27
33
function isWebAuthnSupported ( ) {
28
34
return (
@@ -73,17 +79,19 @@ class Base64Converter {
73
79
}
74
80
75
81
async function webAuthnCreateCredential (
76
- publicKeyOptions : PublicKeyCredentialCreationOptionsWithoutExtensions ,
82
+ publicKeyOptions : __experimental_PublicKeyCredentialCreationOptionsWithoutExtensions ,
77
83
) : Promise < WebAuthnCreateCredentialReturn > {
78
84
try {
79
85
// Typescript types are not aligned with the spec. These type assertions are required to comply with the spec.
80
86
const credential = ( await navigator . credentials . create ( {
81
87
publicKey : publicKeyOptions ,
82
- } ) ) as PublicKeyCredentialWithAuthenticatorAttestationResponse | null ;
88
+ } ) ) as __experimental_PublicKeyCredentialWithAuthenticatorAttestationResponse | null ;
83
89
84
90
if ( ! credential ) {
85
91
return {
86
- error : new ClerkWebAuthnError ( 'Browser failed to create credential' , { code : 'passkey_credential_failed' } ) ,
92
+ error : new ClerkWebAuthnError ( 'Browser failed to create credential' , {
93
+ code : 'passkey_credential_create_failed' ,
94
+ } ) ,
87
95
publicKeyCredential : null ,
88
96
} ;
89
97
}
@@ -94,6 +102,33 @@ async function webAuthnCreateCredential(
94
102
}
95
103
}
96
104
105
+ async function webAuthnGetCredential ( {
106
+ publicKeyOptions,
107
+ conditionalUI,
108
+ } : {
109
+ publicKeyOptions : __experimental_PublicKeyCredentialRequestOptionsWithoutExtensions ;
110
+ conditionalUI : boolean ;
111
+ } ) : Promise < WebAuthnGetCredentialReturn > {
112
+ try {
113
+ // Typescript types are not aligned with the spec. These type assertions are required to comply with the spec.
114
+ const credential = ( await navigator . credentials . get ( {
115
+ publicKey : publicKeyOptions ,
116
+ mediation : conditionalUI ? 'conditional' : 'optional' ,
117
+ } ) ) as __experimental_PublicKeyCredentialWithAuthenticatorAssertionResponse | null ;
118
+
119
+ if ( ! credential ) {
120
+ return {
121
+ error : new ClerkWebAuthnError ( 'Browser failed to get credential' , { code : 'passkey_credential_get_failed' } ) ,
122
+ publicKeyCredential : null ,
123
+ } ;
124
+ }
125
+
126
+ return { publicKeyCredential : credential , error : null } ;
127
+ } catch ( e ) {
128
+ return { error : handlePublicKeyGetError ( e ) , publicKeyCredential : null } ;
129
+ }
130
+ }
131
+
97
132
/**
98
133
* Map webauthn errors from `navigator.credentials.create()` to Clerk-js errors
99
134
* @param error
@@ -107,6 +142,17 @@ function handlePublicKeyCreateError(error: Error): ClerkWebAuthnError | ClerkRun
107
142
return error ;
108
143
}
109
144
145
+ /**
146
+ * Map webauthn errors from `navigator.credentials.get()` to Clerk-js errors
147
+ * @param error
148
+ */
149
+ function handlePublicKeyGetError ( error : Error ) : ClerkWebAuthnError | ClerkRuntimeError | Error {
150
+ if ( error . name === 'NotAllowedError' ) {
151
+ return new ClerkWebAuthnError ( error . message , { code : 'passkey_registration_cancelled' } ) ;
152
+ }
153
+ return error ;
154
+ }
155
+
110
156
function convertJSONToPublicKeyCreateOptions ( jsonPublicKey : PublicKeyCredentialCreationOptionsJSON ) {
111
157
const userIdBuffer = base64UrlToBuffer ( jsonPublicKey . user . id ) ;
112
158
const challengeBuffer = base64UrlToBuffer ( jsonPublicKey . challenge ) ;
@@ -124,16 +170,37 @@ function convertJSONToPublicKeyCreateOptions(jsonPublicKey: PublicKeyCredentialC
124
170
...jsonPublicKey . user ,
125
171
id : userIdBuffer ,
126
172
} ,
127
- } as PublicKeyCredentialCreationOptionsWithoutExtensions ;
173
+ } as __experimental_PublicKeyCredentialCreationOptionsWithoutExtensions ;
174
+ }
175
+
176
+ function convertJSONToPublicKeyRequestOptions ( jsonPublicKey : PublicKeyCredentialRequestOptionsJSON ) {
177
+ const challengeBuffer = base64UrlToBuffer ( jsonPublicKey . challenge ) ;
178
+
179
+ const allowCredentialsWithBuffer = ( jsonPublicKey . allowCredentials || [ ] ) . map ( cred => ( {
180
+ ...cred ,
181
+ id : base64UrlToBuffer ( cred . id ) ,
182
+ } ) ) ;
183
+
184
+ return {
185
+ ...jsonPublicKey ,
186
+ allowCredentials : allowCredentialsWithBuffer ,
187
+ challenge : challengeBuffer ,
188
+ } as __experimental_PublicKeyCredentialRequestOptionsWithoutExtensions ;
128
189
}
129
190
130
- function serializePublicKeyCredential ( publicKeyCredential : PublicKeyCredentialWithAuthenticatorAttestationResponse ) {
131
- const response = publicKeyCredential . response ;
191
+ function __serializePublicKeyCredential < T extends Omit < PublicKeyCredential , 'getClientExtensionResults' > > ( pkc : T ) {
132
192
return {
133
- type : publicKeyCredential . type ,
134
- id : publicKeyCredential . id ,
135
- rawId : bufferToBase64Url ( publicKeyCredential . rawId ) ,
136
- authenticatorAttachment : publicKeyCredential . authenticatorAttachment ,
193
+ type : pkc . type ,
194
+ id : pkc . id ,
195
+ rawId : bufferToBase64Url ( pkc . rawId ) ,
196
+ authenticatorAttachment : pkc . authenticatorAttachment ,
197
+ } ;
198
+ }
199
+
200
+ function serializePublicKeyCredential ( pkc : __experimental_PublicKeyCredentialWithAuthenticatorAttestationResponse ) {
201
+ const response = pkc . response ;
202
+ return {
203
+ ...__serializePublicKeyCredential ( pkc ) ,
137
204
response : {
138
205
clientDataJSON : bufferToBase64Url ( response . clientDataJSON ) ,
139
206
attestationObject : bufferToBase64Url ( response . attestationObject ) ,
@@ -142,6 +209,21 @@ function serializePublicKeyCredential(publicKeyCredential: PublicKeyCredentialWi
142
209
} ;
143
210
}
144
211
212
+ function serializePublicKeyCredentialAssertion (
213
+ pkc : __experimental_PublicKeyCredentialWithAuthenticatorAssertionResponse ,
214
+ ) {
215
+ const response = pkc . response ;
216
+ return {
217
+ ...__serializePublicKeyCredential ( pkc ) ,
218
+ response : {
219
+ clientDataJSON : bufferToBase64Url ( response . clientDataJSON ) ,
220
+ authenticatorData : bufferToBase64Url ( response . authenticatorData ) ,
221
+ signature : bufferToBase64Url ( response . signature ) ,
222
+ userHandle : response . userHandle ? bufferToBase64Url ( response . userHandle ) : null ,
223
+ } ,
224
+ } ;
225
+ }
226
+
145
227
const bufferToBase64Url = Base64Converter . encode . bind ( Base64Converter ) ;
146
228
const base64UrlToBuffer = Base64Converter . decode . bind ( Base64Converter ) ;
147
229
@@ -165,8 +247,9 @@ export {
165
247
bufferToBase64Url ,
166
248
handlePublicKeyCreateError ,
167
249
webAuthnCreateCredential ,
250
+ webAuthnGetCredential ,
168
251
convertJSONToPublicKeyCreateOptions ,
252
+ convertJSONToPublicKeyRequestOptions ,
169
253
serializePublicKeyCredential ,
254
+ serializePublicKeyCredentialAssertion ,
170
255
} ;
171
-
172
- export type { PublicKeyCredentialWithAuthenticatorAttestationResponse } ;
0 commit comments