@@ -160,4 +160,142 @@ final class WebAuthnManagerIntegrationTests: XCTestCase {
160160
161161 // We did it!
162162 }
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+ }
163301}
0 commit comments