diff --git a/Application/Infra/Sources/Service/FunctionAPIClient.swift b/Application/Infra/Sources/Service/FunctionAPIClient.swift index 54812124..b27b5b1f 100644 --- a/Application/Infra/Sources/Service/FunctionAPIClient.swift +++ b/Application/Infra/Sources/Service/FunctionAPIClient.swift @@ -124,6 +124,7 @@ struct FirebaseCustomTokenResponse: Decodable { struct AppleCustomTokenRequest: Encodable { let challengeId: String let authorizationCode: String + let displayName: String? } struct AppleAccountLinkRequest: Encodable { diff --git a/Application/Infra/Sources/Service/SocialLogin/AppleAuthenticationServiceImpl.swift b/Application/Infra/Sources/Service/SocialLogin/AppleAuthenticationServiceImpl.swift index 17f25610..2568933f 100644 --- a/Application/Infra/Sources/Service/SocialLogin/AppleAuthenticationServiceImpl.swift +++ b/Application/Infra/Sources/Service/SocialLogin/AppleAuthenticationServiceImpl.swift @@ -42,38 +42,18 @@ final class AppleAuthenticationServiceImpl: AuthenticationService { let response = try await authenticateWithAppleAsync( hashedNonce: challenge.hashedNonce ) - + + let displayName = response.fullName?.displayName + logger.debug("Requesting custom token from Firebase Function") let customToken = try await requestAppleCustomToken( challengeId: challenge.challengeId, - authorizationCode: response.authorizationCode + authorizationCode: response.authorizationCode, + displayName: displayName ) - + logger.debug("Signing in with custom token") let result = try await Auth.auth().signIn(withCustomToken: customToken) - - let changeRequest = result.user.createProfileChangeRequest() - var displayName: String? - - if let fullName = response.fullName { - let formatter = PersonNameComponentsFormatter() - formatter.style = .long - let formattedName = formatter.string(from: fullName) - if !formattedName.isEmpty { - displayName = formattedName - } - } - - if displayName == nil { - let doc = try await store - .document(FirestorePath.userData(result.user.uid, document: .info)) - .getDocument() - displayName = doc.data()?["appleName"] as? String - } - - changeRequest.displayName = displayName ?? "" - changeRequest.photoURL = nil // Apple ID 프로필 사진 URL은 제공되지 않음 - try await changeRequest.commitChanges() logger.info("Successfully signed in with Apple") return result.user.makeResponse(providerID: .apple) @@ -224,13 +204,15 @@ final class AppleAuthenticationServiceImpl: AuthenticationService { private func requestAppleCustomToken( challengeId: String, - authorizationCode: String + authorizationCode: String, + displayName: String? ) async throws -> String { let response = try await FunctionAPIClient.shared.send( .requestAppleCustomToken, payload: AppleCustomTokenRequest( challengeId: challengeId, - authorizationCode: authorizationCode + authorizationCode: authorizationCode, + displayName: displayName ), requiresAuthentication: false ) @@ -239,6 +221,15 @@ final class AppleAuthenticationServiceImpl: AuthenticationService { } } +extension PersonNameComponents { + var displayName: String? { + let formatter = PersonNameComponentsFormatter() + formatter.style = .long + let name = formatter.string(from: self) + return name.isEmpty ? nil : name + } +} + private extension AppleAuthenticationServiceImpl { func mapAppleAPIError(_ error: Error) -> Error { if let emailError = error.apiEmailError { diff --git a/Application/Infra/Tests/Service/FunctionAPIEndpointTests.swift b/Application/Infra/Tests/Service/FunctionAPIEndpointTests.swift index ad7a1844..15e65d69 100644 --- a/Application/Infra/Tests/Service/FunctionAPIEndpointTests.swift +++ b/Application/Infra/Tests/Service/FunctionAPIEndpointTests.swift @@ -52,16 +52,47 @@ struct FunctionAPIEndpointTests { #expect(endpoint.path == "/auth/apple/access-token") } - @Test("Apple custom token 요청은 challenge와 authorization code만 인코딩한다") - func Apple_custom_token_요청은_challenge와_authorization_code만_인코딩한다() throws { + @Test("Apple custom token 요청은 display name을 포함할 수 있다") + func Apple_custom_token_요청은_display_name을_포함할_수_있다() throws { let request = AppleCustomTokenRequest( challengeId: "challenge-id", - authorizationCode: "authorization-code" + authorizationCode: "authorization-code", + displayName: "Apple User" + ) + + #expect( + try encodedKeys(request) == [ + "challengeId", + "authorizationCode", + "displayName" + ] + ) + } + + @Test("Apple custom token 요청은 없는 display name을 인코딩하지 않는다") + func Apple_custom_token_요청은_없는_display_name을_인코딩하지_않는다() throws { + let request = AppleCustomTokenRequest( + challengeId: "challenge-id", + authorizationCode: "authorization-code", + displayName: nil ) #expect(try encodedKeys(request) == ["challengeId", "authorizationCode"]) } + @Test("Apple 이름은 custom token 요청용 display name으로 변환한다") + func Apple_이름은_custom_token_요청용_display_name으로_변환한다() { + var fullName = PersonNameComponents() + fullName.givenName = "Apple" + + #expect(fullName.displayName == "Apple") + } + + @Test("비어 있는 Apple 이름은 display name으로 변환하지 않는다") + func 비어_있는_Apple_이름은_display_name으로_변환하지_않는다() { + #expect(PersonNameComponents().displayName == nil) + } + @Test("Apple 계정 연결 요청은 credential email을 포함할 수 있다") func Apple_계정_연결_요청은_credential_email을_포함할_수_있다() throws { let request = AppleAccountLinkRequest(