diff --git a/FirebaseAuth/Sources/Swift/Auth/Auth.swift b/FirebaseAuth/Sources/Swift/Auth/Auth.swift index 20dbe473f1a..226d0ea3416 100644 --- a/FirebaseAuth/Sources/Swift/Auth/Auth.swift +++ b/FirebaseAuth/Sources/Swift/Auth/Auth.swift @@ -1695,6 +1695,8 @@ extension Auth: AuthInterop { refreshToken: response.refreshToken, anonymous: false ) + try await user.reload() + try await updateCurrentUser(user) return AuthDataResult(withUser: user, additionalUserInfo: nil) } #endif diff --git a/FirebaseAuth/Sources/Swift/Backend/RPC/FinalizePasskeySignInRequest.swift b/FirebaseAuth/Sources/Swift/Backend/RPC/FinalizePasskeySignInRequest.swift index 1b5cc3c68d1..771849ec4c0 100644 --- a/FirebaseAuth/Sources/Swift/Backend/RPC/FinalizePasskeySignInRequest.swift +++ b/FirebaseAuth/Sources/Swift/Backend/RPC/FinalizePasskeySignInRequest.swift @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import Foundation /// The GCIP endpoint for finalizePasskeySignIn rpc private let finalizePasskeySignInEndPoint = "accounts/passkeySignIn:finalize" @@ -50,19 +51,21 @@ class FinalizePasskeySignInRequest: IdentityToolkitRequest, AuthRPCRequest { } var unencodedHTTPRequestBody: [String: AnyHashable]? { + let assertion: [String: AnyHashable] = [ + "clientDataJSON": clientDataJSON, + "authenticatorData": authenticatorData, + "signature": signature, + "userHandle": userId, + ] + let authResponse: [String: AnyHashable] = [ + "id": credentialID, + "response": assertion, + ] var postBody: [String: AnyHashable] = [ - "authenticatorAssertionResponse": [ - "credentialId": credentialID, - "authenticatorAssertionResponse": [ - "clientDataJSON": clientDataJSON, - "authenticatorData": authenticatorData, - "signature": signature, - "userHandle": userId, - ], - ] as [String: AnyHashable], + "authenticatorAuthenticationResponse": authResponse, ] - if let tenantID = tenantID { - postBody["tenantId"] = tenantID + if let tenant = tenantID { + postBody["tenantId"] = tenant } return postBody } diff --git a/FirebaseAuth/Sources/Swift/Backend/RPC/FinalizePasskeySignInResponse.swift b/FirebaseAuth/Sources/Swift/Backend/RPC/FinalizePasskeySignInResponse.swift index f172a9a3259..6d0b772ee9c 100644 --- a/FirebaseAuth/Sources/Swift/Backend/RPC/FinalizePasskeySignInResponse.swift +++ b/FirebaseAuth/Sources/Swift/Backend/RPC/FinalizePasskeySignInResponse.swift @@ -14,6 +14,8 @@ * limitations under the License. */ +import Foundation + @available(iOS 15.0, macOS 12.0, tvOS 16.0, *) struct FinalizePasskeySignInResponse: AuthRPCResponse { /// The user raw access token. diff --git a/FirebaseAuth/Sources/Swift/Backend/RPC/GetAccountInfoResponse.swift b/FirebaseAuth/Sources/Swift/Backend/RPC/GetAccountInfoResponse.swift index 63d419e740e..012d4cda950 100644 --- a/FirebaseAuth/Sources/Swift/Backend/RPC/GetAccountInfoResponse.swift +++ b/FirebaseAuth/Sources/Swift/Backend/RPC/GetAccountInfoResponse.swift @@ -136,7 +136,7 @@ struct GetAccountInfoResponse: AuthRPCResponse { } else { mfaEnrollments = nil } - if let passkeyEnrollmentData = dictionary["passkeys"] as? [[String: AnyHashable]] { + if let passkeyEnrollmentData = dictionary["passkeyInfo"] as? [[String: AnyHashable]] { enrolledPasskeys = passkeyEnrollmentData.map { PasskeyInfo(dictionary: $0) } } else { enrolledPasskeys = nil diff --git a/FirebaseAuth/Sources/Swift/Backend/RPC/StartPasskeySignInRequest.swift b/FirebaseAuth/Sources/Swift/Backend/RPC/StartPasskeySignInRequest.swift index 4b317460681..e36dc40caf8 100644 --- a/FirebaseAuth/Sources/Swift/Backend/RPC/StartPasskeySignInRequest.swift +++ b/FirebaseAuth/Sources/Swift/Backend/RPC/StartPasskeySignInRequest.swift @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +import Foundation + /// The GCIP endpoint for startPasskeySignIn rpc private let startPasskeySignInEndpoint = "accounts/passkeySignIn:start" @@ -29,7 +31,7 @@ class StartPasskeySignInRequest: IdentityToolkitRequest, AuthRPCRequest { var unencodedHTTPRequestBody: [String: AnyHashable]? { guard let tenantID = tenantID else { - return nil + return [:] } return ["tenantId": tenantID] } diff --git a/FirebaseAuth/Sources/Swift/Backend/RPC/StartPasskeySignInResponse.swift b/FirebaseAuth/Sources/Swift/Backend/RPC/StartPasskeySignInResponse.swift index 096e674ee56..7461425312e 100644 --- a/FirebaseAuth/Sources/Swift/Backend/RPC/StartPasskeySignInResponse.swift +++ b/FirebaseAuth/Sources/Swift/Backend/RPC/StartPasskeySignInResponse.swift @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +import Foundation + @available(iOS 15.0, macOS 12.0, tvOS 16.0, *) struct StartPasskeySignInResponse: AuthRPCResponse { /// The RP ID of the FIDO Relying Party diff --git a/FirebaseAuth/Sources/Swift/User/User.swift b/FirebaseAuth/Sources/Swift/User/User.swift index ba6036a5528..c2378fcad1c 100644 --- a/FirebaseAuth/Sources/Swift/User/User.swift +++ b/FirebaseAuth/Sources/Swift/User/User.swift @@ -1079,12 +1079,7 @@ extension User: NSSecureCoding {} requestConfiguration: requestConfiguration ) let response = try await backend.call(with: request) - guard let passkeyName = (name?.isEmpty ?? true) ? defaultPasskeyName : name - else { throw NSError( - domain: AuthErrorDomain, - code: AuthErrorCode.internalError.rawValue, - userInfo: [NSLocalizedDescriptionKey: "Failed to unwrap passkey name"] - ) } + passkeyName = (name?.isEmpty ?? true) ? defaultPasskeyName : name guard let challengeInData = Data(base64Encoded: response.challenge) else { throw NSError( domain: AuthErrorDomain, @@ -1104,7 +1099,7 @@ extension User: NSSecureCoding {} ) return provider.createCredentialRegistrationRequest( challenge: challengeInData, - name: passkeyName, + name: passkeyName ?? defaultPasskeyName, userID: userIdInData ) } @@ -1114,13 +1109,26 @@ extension User: NSSecureCoding {} @available(iOS 15.0, macOS 12.0, tvOS 16.0, *) public func finalizePasskeyEnrollment(withPlatformCredential platformCredential: ASAuthorizationPlatformPublicKeyCredentialRegistration) async throws -> AuthDataResult { + guard + !platformCredential.credentialID.isEmpty, + !platformCredential.rawClientDataJSON.isEmpty, + let attestation = platformCredential.rawAttestationObject, + !attestation.isEmpty + else { + throw NSError( + domain: AuthErrorDomain, + code: AuthErrorCode.internalError.rawValue, + userInfo: [NSLocalizedDescriptionKey: + "Invalid platform credential: missing credentialID, clientDataJSON, or attestationObject."] + ) + } let credentialID = platformCredential.credentialID.base64EncodedString() let clientDataJSON = platformCredential.rawClientDataJSON.base64EncodedString() let attestationObject = platformCredential.rawAttestationObject!.base64EncodedString() let request = FinalizePasskeyEnrollmentRequest( idToken: rawAccessToken(), - name: passkeyName ?? "Unnamed account (Apple)", + name: passkeyName ?? defaultPasskeyName, credentialID: credentialID, clientDataJSON: clientDataJSON, attestationObject: attestationObject, @@ -1133,6 +1141,9 @@ extension User: NSSecureCoding {} refreshToken: response.refreshToken, anonymous: false ) + defer { self.passkeyName = nil } + try await user.reload() + try await auth!.updateCurrentUser(user) return AuthDataResult(withUser: user, additionalUserInfo: nil) } @@ -1147,12 +1158,14 @@ extension User: NSSecureCoding {} request.deletePasskeys = [credentialID] request.accessToken = rawAccessToken() let response = try await backend.call(with: request) - _ = try await auth!.completeSignIn( + let user = try await auth!.completeSignIn( withAccessToken: response.idToken, accessTokenExpirationDate: response.approximateExpirationDate, refreshToken: response.refreshToken, anonymous: false ) + try await user.reload() + try await auth!.updateCurrentUser(user) } #endif diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Utility/Extensions.swift b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Utility/Extensions.swift index 7ee8aa941d3..4fa1503e0e0 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Utility/Extensions.swift +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/Utility/Extensions.swift @@ -33,14 +33,6 @@ extension User: DataSourceProvidable { return Section(headerDescription: "Info", items: items) } - private var metaDataSection: Section { - let metadataRows = [ - Item(title: metadata.lastSignInDate?.description, detailTitle: "Last Sign-in Date"), - Item(title: metadata.creationDate?.description, detailTitle: "Creation Date"), - ] - return Section(headerDescription: "Firebase Metadata", items: metadataRows) - } - private var passkeysSection: Section { let passkeys = enrolledPasskeys ?? [] guard !passkeys.isEmpty else { @@ -55,6 +47,14 @@ extension User: DataSourceProvidable { return Section(headerDescription: "Passkeys", items: items) } + private var metaDataSection: Section { + let metadataRows = [ + Item(title: metadata.lastSignInDate?.description, detailTitle: "Last Sign-in Date"), + Item(title: metadata.creationDate?.description, detailTitle: "Creation Date"), + ] + return Section(headerDescription: "Firebase Metadata", items: metadataRows) + } + private var otherSection: Section { let otherRows = [Item(title: isAnonymous ? "Yes" : "No", detailTitle: "Is User Anonymous?"), Item(title: isEmailVerified ? "Yes" : "No", detailTitle: "Is Email Verified?")] @@ -76,7 +76,7 @@ extension User: DataSourceProvidable { } var sections: [Section] { - [infoSection, metaDataSection, passkeysSection, otherSection, actionSection] + [infoSection, passkeysSection, metaDataSection, otherSection, actionSection] } } diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AuthViewController.swift b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AuthViewController.swift index bd0d14fff6a..bb3fc378c91 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AuthViewController.swift +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AuthViewController.swift @@ -962,10 +962,7 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate { showAlert(for: "Please sign in first.") return } - guard let passkeyName = await showTextInputPrompt(with: "Passkey name") else { - print("Passkey enrollment cancelled: no name entered.") - return - } + let passkeyName = await showTextInputPrompt(with: "Passkey name") guard #available(iOS 16.0, macOS 12.0, tvOS 16.0, *) else { showAlert(for: "Not Supported", message: "This OS version does not support passkeys.") return @@ -989,16 +986,15 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate { print("OS version is not supported for this action.") return } - Task { - do { - let request = try await AppManager.shared.auth().startPasskeySignIn() - let controller = ASAuthorizationController(authorizationRequests: [request]) - controller.delegate = self - controller.presentationContextProvider = self - controller.performRequests(options: .preferImmediatelyAvailableCredentials) - } catch { - print("Passkey sign-in failed with error: \(error)") - } + do { + let request = try await AppManager.shared.auth().startPasskeySignIn() + let controller = ASAuthorizationController(authorizationRequests: [request]) + controller.delegate = self + controller.presentationContextProvider = self + controller.performRequests() + print("Started passkey sign in (challenge created).") + } catch { + print("Passkey sign-in failed with error: \(error)") } } @@ -1144,7 +1140,23 @@ extension AuthViewController: ASAuthorizationControllerDelegate, } return } - + if let assertion = authorization + .credential as? ASAuthorizationPlatformPublicKeyCredentialAssertion { + Task { @MainActor [weak self] in + guard let self else { return } + do { + let _ = try await AppManager.shared.auth() + .finalizePasskeySignIn(withPlatformCredential: assertion) + self.showAlert(for: "Passkey Sign-In", message: "Succeeded") + print("Passkey sign-in succeeded.") + self.transitionToUserViewController() + } catch { + self.showAlert(for: "Passkey Sign-In failed", message: error.localizedDescription) + print("Finalize passkey sign-in failed: \(error.localizedDescription)") + } + } + return + } guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential else { print("Unable to retrieve AppleIDCredential") diff --git a/FirebaseAuth/Tests/SampleSwift/AuthenticationExampleUITests/AuthenticationExampleUITests.swift b/FirebaseAuth/Tests/SampleSwift/AuthenticationExampleUITests/AuthenticationExampleUITests.swift index d7c893d20c8..d8d6b6eecf8 100644 --- a/FirebaseAuth/Tests/SampleSwift/AuthenticationExampleUITests/AuthenticationExampleUITests.swift +++ b/FirebaseAuth/Tests/SampleSwift/AuthenticationExampleUITests/AuthenticationExampleUITests.swift @@ -309,6 +309,37 @@ class AuthenticationExampleUITests: XCTestCase { ) } + func testPasskeyList() { + signOut() + let testEmail = "sample.ios.auth@gmail.com" + let testPassword = "sample.ios.auth" + let testPasskeyName = "sampleiosauth" + app.staticTexts["Email & Password Login"].tap() + app.textFields["Email"].tap() + app.textFields["Email"].typeText(testEmail) + app.textFields["Password"].tap() + app.textFields["Password"].typeText(testPassword) + app.buttons["Login"].tap() + wait(forElement: app.navigationBars["User"], timeout: 5.0) + XCTAssertTrue(app.navigationBars["User"].exists) + XCTAssertTrue( + app.staticTexts[testEmail].exists, + "The user should be signed in and the email field should display their email." + ) + let userTable = app.tables.firstMatch + XCTAssertTrue(userTable.waitForExistence(timeout: 5.0), "User detail list should exist") + let passkeyLabel = userTable.staticTexts[testPasskeyName] + if !passkeyLabel.exists { + for _ in 0 ..< 5 where !passkeyLabel.exists { + userTable.swipeUp() + } + } + XCTAssertTrue( + passkeyLabel.waitForExistence(timeout: 5.0), + "Passkey named '\(testPasskeyName)' should be visible in the Passkeys section." + ) + } + // MARK: - Private Helpers private func signOut() { diff --git a/FirebaseAuth/Tests/SampleSwift/SwiftApiTests/PasskeyTests.swift b/FirebaseAuth/Tests/SampleSwift/SwiftApiTests/PasskeyTests.swift index 45ab6413602..bee4e2f4d91 100644 --- a/FirebaseAuth/Tests/SampleSwift/SwiftApiTests/PasskeyTests.swift +++ b/FirebaseAuth/Tests/SampleSwift/SwiftApiTests/PasskeyTests.swift @@ -23,8 +23,6 @@ @available(iOS 15.0, macOS 12.0, tvOS 16.0, *) class PasskeyTests: TestsBase { - // MARK: Enrollment Tests - @available(iOS 15.0, macOS 12.0, tvOS 16.0, *) func testStartPasskeyEnrollmentSuccess() async throws { try await signInAnonymouslyAsync() @@ -41,7 +39,7 @@ } @available(iOS 15.0, macOS 12.0, tvOS 16.0, *) - func testStartPasskeyEnrollmentFailureWithInvalidToken() async throws { + func DRAFTtestStartPasskeyEnrollmentFailureWithInvalidToken() async throws { try await signInAnonymouslyAsync() guard let user = Auth.auth().currentUser else { XCTFail("Expected a signed-in user") @@ -119,6 +117,128 @@ } try? await deleteCurrentUserAsync() } + + @available(iOS 15.0, macOS 12.0, tvOS 16.0, *) + func testStartPasskeySignInSuccess() async throws { + let assertionRequest = try await Auth.auth().startPasskeySignIn() + XCTAssertFalse(assertionRequest.relyingPartyIdentifier.isEmpty, + "rpID should be non-empty") + XCTAssertFalse(assertionRequest.challenge.isEmpty, + "challenge should be non-empty") + XCTAssertTrue(assertionRequest + is ASAuthorizationPlatformPublicKeyCredentialAssertionRequest) + } + + @available(iOS 15.0, macOS 12.0, tvOS 16.0, *) + func testFinalizePasskeySignInFailureInvalidAttestation() async throws { + let auth = Auth.auth() + let config = auth.requestConfiguration + let badRequest = FinalizePasskeySignInRequest( + credentialID: "fakeCredentialId".data(using: .utf8)!.base64EncodedString(), + clientDataJSON: "fakeClientData".data(using: .utf8)!.base64EncodedString(), + authenticatorData: "fakeAuthenticatorData".data(using: .utf8)!.base64EncodedString(), + signature: "fakeSignature".data(using: .utf8)!.base64EncodedString(), + userId: "fakeUID".data(using: .utf8)!.base64EncodedString(), + requestConfiguration: config + ) + do { + _ = try await auth.backend.call(with: badRequest) + } catch { + let ns = error as NSError + if let code = AuthErrorCode(rawValue: ns.code) { + XCTAssertEqual(code, .userNotFound) + } + } + } + + @available(iOS 15.0, macOS 12.0, tvOS 16.0, *) + func testFinalizePasskeySignInFailureWithoutAttestation() async throws { + let auth = Auth.auth() + let config = auth.requestConfiguration + let badRequest = FinalizePasskeySignInRequest( + credentialID: "", + clientDataJSON: "", + authenticatorData: "", + signature: "", + userId: "", + requestConfiguration: config + ) + do { + _ = try await auth.backend.call(with: badRequest) + } catch { + let ns = error as NSError + if let code = AuthErrorCode(rawValue: ns.code) { + XCTAssertEqual(code, .userNotFound) + } + } + } + + @available(iOS 15.0, macOS 12.0, tvOS 16.0, *) + func DRAFTtestUnenrollPasskeySuccess() async throws { + let testEmail = "sample.ios.auth@gmail.com" + let testPassword = "sample.ios.auth" + let testCredentialId = "cred_id" + let auth = Auth.auth() + try await auth.signIn(withEmail: testEmail, password: testPassword) + guard let user = Auth.auth().currentUser else { + XCTFail("Expected a signed-in user") + return + } + try? await user.reload() + let prevPasskeys = user.enrolledPasskeys ?? [] + XCTAssertTrue( + prevPasskeys.contains { $0.credentialID == testCredentialId }, + "Precondition failed: passkey \(testCredentialId) is not enrolled on this account." + ) + let prevCount = prevPasskeys.count + let _ = try await user.unenrollPasskey(withCredentialID: testCredentialId) + try? await user.reload() + let updatedPasskeys = user.enrolledPasskeys ?? [] + XCTAssertFalse( + updatedPasskeys.contains { $0.credentialID == testCredentialId }, + "Passkey \(testCredentialId) should be removed after unenroll." + ) + XCTAssertEqual( + updatedPasskeys.count, prevCount - 1, + "Exactly one passkey should have been removed." + ) + XCTAssertNil( + updatedPasskeys.first { $0.credentialID == testCredentialId }, + "Passkey \(testCredentialId) should not exist after unenroll." + ) + } + + @available(iOS 15.0, macOS 12.0, tvOS 16.0, *) + func testUnenrollPasskeyFailure() async throws { + let testEmail = "sample.ios.auth@gmail.com" + let testPassword = "sample.ios.auth" + let testCredentialId = "FCBopZ3mzjfPNXqWXXjAM/ZnnlQ=" + let auth = Auth.auth() + try await auth.signIn(withEmail: testEmail, password: testPassword) + guard let user = Auth.auth().currentUser else { + XCTFail("Expected a signed-in user") + return + } + try? await user.reload() + do { + let _ = try await user.unenrollPasskey(withCredentialID: testCredentialId) + XCTFail("Expected invalid passkey error") + } catch { + let ns = error as NSError + if let code = AuthErrorCode(rawValue: ns.code) { + XCTAssertEqual(code, .missingPasskeyEnrollment, + "Expected .missingPasskeyEnrollment, got \(code)") + } + let message = (ns.userInfo[NSLocalizedDescriptionKey] as? String ?? "").uppercased() + XCTAssertTrue( + message + .contains( + "CANNOT FIND THE PASSKEY LINKED TO THE CURRENT ACCOUNT" + ), + "Expected Missing Passkey Enrollment error, got: \(message)" + ) + } + } } #endif diff --git a/FirebaseAuth/Tests/Unit/FinalizePasskeySignInRequestTests.swift b/FirebaseAuth/Tests/Unit/FinalizePasskeySignInRequestTests.swift index a077f1784fb..277ba0f36b7 100644 --- a/FirebaseAuth/Tests/Unit/FinalizePasskeySignInRequestTests.swift +++ b/FirebaseAuth/Tests/Unit/FinalizePasskeySignInRequestTests.swift @@ -71,33 +71,38 @@ userId: kUserId, requestConfiguration: fakeConfig ) - let body = request.unencodedHTTPRequestBody - XCTAssertNotNil(body) - let authnAssertionResp = body?["authenticatorAssertionResponse"] as? [String: AnyHashable] - XCTAssertNotNil(authnAssertionResp) - XCTAssertEqual(authnAssertionResp?["credentialId"] as? String, kCredentialID) - let innerResponse = - authnAssertionResp?["authenticatorAssertionResponse"] as? [String: AnyHashable] - XCTAssertNotNil(innerResponse) - XCTAssertEqual(innerResponse?["clientDataJSON"] as? String, kClientDataJSON) - XCTAssertEqual(innerResponse?["authenticatorData"] as? String, kAuthenticatorData) - XCTAssertEqual(innerResponse?["signature"] as? String, kSignature) - XCTAssertEqual(innerResponse?["userHandle"] as? String, kUserId) - XCTAssertNil(body?["tenantId"]) + guard let postBody = request.unencodedHTTPRequestBody else { + return XCTFail("Body should not be nil") + } + guard let authnAssertionResp = + postBody["authenticatorAuthenticationResponse"] as? [String: AnyHashable] else { + return XCTFail("Missing authenticatorAuthenticationResponse") + } + XCTAssertEqual(authnAssertionResp["id"] as? String, kCredentialID) + guard let response = authnAssertionResp["response"] as? [String: AnyHashable] else { + return XCTFail("Missing nested response dictionary") + } + XCTAssertEqual(response["clientDataJSON"] as? String, kClientDataJSON) + XCTAssertEqual(response["authenticatorData"] as? String, kAuthenticatorData) + XCTAssertEqual(response["signature"] as? String, kSignature) + XCTAssertEqual(response["userHandle"] as? String, kUserId) + XCTAssertNil(postBody["tenantId"]) // no tenant by default } func testUnencodedHTTPRequestBodyWithTenantId() { - let options = FirebaseOptions(googleAppID: "0:0000000000000:ios:0000000000000000", - gcmSenderID: "00000000000000000-00000000000-000000000") + let options = FirebaseOptions( + googleAppID: "0:0000000000000:ios:0000000000000000", + gcmSenderID: "00000000000000000-00000000000-000000000" + ) options.apiKey = "FAKE_API_KEY" options.projectID = "myProjectID" - let fakeApp = FirebaseApp(instanceWithName: "testApp", options: options) - let fakeAuth = Auth(app: fakeApp) - fakeAuth.tenantID = "TEST_TENANT" + let app = FirebaseApp(instanceWithName: "testApp", options: options) + let auth = Auth(app: app) + auth.tenantID = "TEST_TENANT" let configWithTenant = AuthRequestConfiguration( apiKey: "FAKE_API_KEY", appID: "FAKE_APP_ID", - auth: fakeAuth + auth: auth ) request = FinalizePasskeySignInRequest( credentialID: kCredentialID, @@ -107,9 +112,22 @@ userId: kUserId, requestConfiguration: configWithTenant ) - - let body = request.unencodedHTTPRequestBody - XCTAssertEqual(body?["tenantId"] as? String, "TEST_TENANT") + guard let body = request.unencodedHTTPRequestBody else { + return XCTFail("Body should not be nil") + } + XCTAssertEqual(body["tenantId"] as? String, "TEST_TENANT") + // also checking structure remains same with tenant + guard let top = body["authenticatorAuthenticationResponse"] as? [String: AnyHashable] else { + return XCTFail("Missing authenticatorAuthenticationResponse") + } + XCTAssertEqual(top["id"] as? String, kCredentialID) + guard let response = top["response"] as? [String: AnyHashable] else { + return XCTFail("Missing nested response dictionary") + } + XCTAssertEqual(response["clientDataJSON"] as? String, kClientDataJSON) + XCTAssertEqual(response["authenticatorData"] as? String, kAuthenticatorData) + XCTAssertEqual(response["signature"] as? String, kSignature) + XCTAssertEqual(response["userHandle"] as? String, kUserId) } } diff --git a/FirebaseAuth/Tests/Unit/GetAccountInfoTests.swift b/FirebaseAuth/Tests/Unit/GetAccountInfoTests.swift index 764fdaad55d..46390ab8b89 100644 --- a/FirebaseAuth/Tests/Unit/GetAccountInfoTests.swift +++ b/FirebaseAuth/Tests/Unit/GetAccountInfoTests.swift @@ -80,11 +80,11 @@ class GetAccountInfoTests: RPCBaseTests { let kEmailVerifiedKey = "emailVerified" let kLocalIDKey = "localId" let kTestLocalID = "testLocalId" - let kPasskeysKey = "passkeys" + let kPasskeysKey = "passkeyInfo" // Fake PasskeyInfo let testCredentialId = "credential_id" - let testPasskeyName = "Test Passkey" + let testPasskeyName = "testPasskey" let passkeys = [[ "credentialId": testCredentialId, "name": testPasskeyName, @@ -141,7 +141,7 @@ class GetAccountInfoTests: RPCBaseTests { let userDict: [String: AnyHashable] = [ "localId": "user123", "email": "user@example.com", - "passkeys": [passkey1, passkey2], + "passkeyInfo": [passkey1, passkey2], ] let dict: [String: AnyHashable] = ["users": [userDict]] let response = try GetAccountInfoResponse(dictionary: dict) diff --git a/FirebaseAuth/Tests/Unit/StartPasskeySignInRequestTests.swift b/FirebaseAuth/Tests/Unit/StartPasskeySignInRequestTests.swift index 40fbe92f26f..8d3b370b1bc 100644 --- a/FirebaseAuth/Tests/Unit/StartPasskeySignInRequestTests.swift +++ b/FirebaseAuth/Tests/Unit/StartPasskeySignInRequestTests.swift @@ -68,7 +68,7 @@ func testUnencodedHTTPRequestBody_WithoutTenantId() { let request = StartPasskeySignInRequest(requestConfiguration: config) - XCTAssertNil(request.unencodedHTTPRequestBody) + XCTAssertEqual(request.unencodedHTTPRequestBody, [:]) } }