@@ -160,4 +160,142 @@ final class WebAuthnManagerIntegrationTests: XCTestCase {
160
160
161
161
// We did it!
162
162
}
163
+
164
+ func testClientRegistrationAndAuthentication( ) async throws {
165
+ let challenge : [ UInt8 ] = [ 1 , 0 , 1 ]
166
+ let relyingPartyDisplayName = " Testy test "
167
+ let relyingPartyID = " example.com "
168
+ let relyingPartyOrigin = " https://example.com "
169
+
170
+ let server = WebAuthnManager (
171
+ configuration: . init(
172
+ relyingPartyID: relyingPartyID,
173
+ relyingPartyName: relyingPartyDisplayName,
174
+ relyingPartyOrigin: relyingPartyOrigin
175
+ ) ,
176
+ challengeGenerator: . mock( generate: challenge)
177
+ )
178
+
179
+ let client = WebAuthnClient ( )
180
+ let aaguid = AAGUID ( uuid: UUID ( ) )
181
+ let authenticator = KeyPairAuthenticator ( attestationGloballyUniqueID: aaguid)
182
+
183
+ let credentialCreationOptions = server. beginRegistration ( user: . init( id: [ 1 , 2 , 3 ] , name: " 123 " , displayName: " One Two Three " ) )
184
+
185
+ let ( registrationCredential, credentialSource) = try await client. createRegistrationCredential (
186
+ options: credentialCreationOptions,
187
+ origin: relyingPartyOrigin,
188
+ authenticator: authenticator
189
+ )
190
+
191
+ XCTAssertEqual ( registrationCredential. type, . publicKey)
192
+ XCTAssertEqual ( registrationCredential. rawID. count, 16 )
193
+ XCTAssertEqual ( registrationCredential. id, registrationCredential. rawID. base64URLEncodedString ( ) )
194
+
195
+ let parsedAttestationResponse = try ParsedAuthenticatorAttestationResponse ( from: registrationCredential. attestationResponse)
196
+ XCTAssertEqual ( parsedAttestationResponse. clientData. type, . create)
197
+ XCTAssertEqual ( parsedAttestationResponse. clientData. challenge. decodedBytes, [ 1 , 0 , 1 ] )
198
+ XCTAssertEqual ( parsedAttestationResponse. clientData. origin, " https://example.com " )
199
+
200
+ XCTAssertEqual ( parsedAttestationResponse. attestationObject. authenticatorData. relyingPartyIDHash, [ 163 , 121 , 166 , 246 , 238 , 175 , 185 , 165 , 94 , 55 , 140 , 17 , 128 , 52 , 226 , 117 , 30 , 104 , 47 , 171 , 159 , 45 , 48 , 171 , 19 , 210 , 18 , 85 , 134 , 206 , 25 , 71 ] )
201
+ XCTAssertEqual ( parsedAttestationResponse. attestationObject. authenticatorData. flags. bytes, [ 0b01011101 ] )
202
+ XCTAssertEqual ( parsedAttestationResponse. attestationObject. authenticatorData. counter, 0 )
203
+ XCTAssertNotNil ( parsedAttestationResponse. attestationObject. authenticatorData. attestedData)
204
+ XCTAssertEqual ( parsedAttestationResponse. attestationObject. authenticatorData. attestedData? . authenticatorAttestationGUID, AAGUID . anonymous)
205
+ XCTAssertEqual ( parsedAttestationResponse. attestationObject. authenticatorData. attestedData? . credentialID, credentialSource. id. bytes)
206
+ XCTAssertEqual ( parsedAttestationResponse. attestationObject. authenticatorData. extData, nil )
207
+
208
+ let publicKey = try CredentialPublicKey ( publicKeyBytes: parsedAttestationResponse. attestationObject. authenticatorData. attestedData? . publicKey ?? [ ] )
209
+ if case . ec2( let key) = publicKey {
210
+ XCTAssertEqual ( key. algorithm, . algES256)
211
+ XCTAssertEqual ( key. curve, . p256)
212
+ XCTAssertEqual ( key. xCoordinate. count, 32 )
213
+ XCTAssertEqual ( key. yCoordinate. count, 32 )
214
+ XCTAssertEqual ( key. rawRepresentation, ( credentialSource. publicKey as? EC2PublicKey ) ? . rawRepresentation)
215
+ } else {
216
+ XCTFail ( " Unexpected publicKey format " )
217
+ }
218
+
219
+ XCTAssertEqual ( parsedAttestationResponse. attestationObject. format, . none)
220
+ XCTAssertEqual ( parsedAttestationResponse. attestationObject. attestationStatement, [ : ] )
221
+
222
+ XCTAssertEqual ( credentialSource. relyingPartyID, " example.com " )
223
+ XCTAssertEqual ( credentialSource. userHandle, [ 1 , 2 , 3 ] )
224
+ XCTAssertEqual ( credentialSource. counter, 0 )
225
+ if case . es256( let privateKey) = credentialSource. key {
226
+ XCTAssertEqual ( Array ( privateKey. publicKey. rawRepresentation) , ( credentialSource. publicKey as? EC2PublicKey ) ? . rawRepresentation)
227
+ } else {
228
+ XCTFail ( " Unexpected credentialSource.key format " )
229
+ }
230
+
231
+ let registeredCredential = try await server. finishRegistration (
232
+ challenge: challenge,
233
+ credentialCreationData: registrationCredential
234
+ ) { credentialID in
235
+ XCTAssertEqual ( credentialID, credentialSource. id. bytes. base64URLEncodedString ( ) . asString ( ) )
236
+ return true
237
+ }
238
+
239
+ XCTAssertEqual ( registeredCredential. type, . publicKey)
240
+ XCTAssertEqual ( registeredCredential. id, credentialSource. id. bytes. base64EncodedString ( ) . asString ( ) )
241
+ XCTAssertEqual ( registeredCredential. publicKey, ( credentialSource. publicKey as? EC2PublicKey ) ? . bytes)
242
+ XCTAssertEqual ( registeredCredential. signCount, 0 )
243
+ XCTAssertEqual ( registeredCredential. backupEligible, true )
244
+ XCTAssertEqual ( registeredCredential. isBackedUp, true )
245
+
246
+ let credentialRequestOptions = try server. beginAuthentication ( )
247
+
248
+ XCTAssertEqual ( credentialRequestOptions. challenge, [ 1 , 0 , 1 ] )
249
+ XCTAssertEqual ( credentialRequestOptions. timeout, . milliseconds( 60000 ) )
250
+ XCTAssertEqual ( credentialRequestOptions. relyingPartyID, " example.com " )
251
+ XCTAssertNil ( credentialRequestOptions. allowCredentials)
252
+ XCTAssertEqual ( credentialRequestOptions. userVerification, . preferred)
253
+
254
+ let ( authenticationCredential, updatedCredentialSource) = try await client. assertAuthenticationCredential (
255
+ options: credentialRequestOptions,
256
+ origin: relyingPartyOrigin,
257
+ authenticator: authenticator,
258
+ credentialStore: [ credentialSource. id : credentialSource]
259
+ )
260
+
261
+ XCTAssertEqual ( authenticationCredential. type, . publicKey)
262
+ XCTAssertEqual ( authenticationCredential. rawID. count, 16 )
263
+ XCTAssertEqual ( authenticationCredential. id, authenticationCredential. rawID. base64URLEncodedString ( ) )
264
+ XCTAssertEqual ( authenticationCredential. authenticatorAttachment, . platform)
265
+
266
+ let parsedAssertionResponse = try ParsedAuthenticatorAssertionResponse ( from: authenticationCredential. response)
267
+ XCTAssertEqual ( parsedAssertionResponse. clientData. type, . assert)
268
+ XCTAssertEqual ( parsedAssertionResponse. clientData. challenge. decodedBytes, [ 1 , 0 , 1 ] )
269
+ XCTAssertEqual ( parsedAssertionResponse. clientData. origin, " https://example.com " )
270
+
271
+ XCTAssertEqual ( parsedAssertionResponse. authenticatorData. relyingPartyIDHash, [ 163 , 121 , 166 , 246 , 238 , 175 , 185 , 165 , 94 , 55 , 140 , 17 , 128 , 52 , 226 , 117 , 30 , 104 , 47 , 171 , 159 , 45 , 48 , 171 , 19 , 210 , 18 , 85 , 134 , 206 , 25 , 71 ] )
272
+ XCTAssertEqual ( parsedAssertionResponse. authenticatorData. flags. bytes, [ 0b00011101 ] )
273
+ XCTAssertEqual ( parsedAssertionResponse. authenticatorData. counter, 0 )
274
+ XCTAssertNil ( parsedAssertionResponse. authenticatorData. attestedData)
275
+ XCTAssertNil ( parsedAssertionResponse. authenticatorData. extData)
276
+
277
+ XCTAssertNotNil ( parsedAssertionResponse. signature. decodedBytes)
278
+ XCTAssertEqual ( parsedAssertionResponse. userHandle, [ 1 , 2 , 3 ] )
279
+
280
+ XCTAssertEqual ( credentialSource. id, updatedCredentialSource. id)
281
+ XCTAssertEqual ( updatedCredentialSource. relyingPartyID, " example.com " )
282
+ XCTAssertEqual ( updatedCredentialSource. userHandle, [ 1 , 2 , 3 ] )
283
+ XCTAssertEqual ( updatedCredentialSource. counter, 0 )
284
+ if case . es256( let privateKey) = updatedCredentialSource. key {
285
+ XCTAssertEqual ( Array ( privateKey. publicKey. rawRepresentation) , ( updatedCredentialSource. publicKey as? EC2PublicKey ) ? . rawRepresentation)
286
+ } else {
287
+ XCTFail ( " Unexpected credentialSource.key format " )
288
+ }
289
+
290
+ let verifiedAuthentication = try server. finishAuthentication (
291
+ credential: authenticationCredential,
292
+ expectedChallenge: challenge,
293
+ credentialPublicKey: registeredCredential. publicKey, credentialCurrentSignCount: registeredCredential. signCount
294
+ )
295
+
296
+ XCTAssertEqual ( verifiedAuthentication. credentialID. urlDecoded. asString ( ) , registeredCredential. id)
297
+ XCTAssertEqual ( verifiedAuthentication. newSignCount, 0 )
298
+ XCTAssertEqual ( verifiedAuthentication. credentialDeviceType, . multiDevice)
299
+ XCTAssertEqual ( verifiedAuthentication. credentialBackedUp, true )
300
+ }
163
301
}
0 commit comments