Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions Application/App/Sources/Resource/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
<dict>
<key>TESTFLIGHT_URL</key>
<string>$(TESTFLIGHT_URL)</string>
<key>APP_REDIRECT_URL</key>
<string>$(APP_REDIRECT_URL)</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
Expand Down Expand Up @@ -46,8 +44,6 @@
<string>$(FUNCTION_API_BASE_URL)</string>
<key>GIDClientID</key>
<string>$(CLIENT_ID)</string>
<key>GITHUB_CLIENT_ID</key>
<string>$(GITHUB_CLIENT_ID)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationCategoryType</key>
Expand Down
5 changes: 1 addition & 4 deletions Application/Data/Sources/DTO/AuthDataResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,20 @@ public struct AuthDataResponse {
public let providers: [String]
public let providerID: String
public let fcmToken: String?
public let accessToken: String?

public init(
uid: String,
displayName: String?,
email: String?,
providers: [String],
providerID: String,
fcmToken: String? = nil,
accessToken: String? = nil
fcmToken: String? = nil
) {
self.uid = uid
self.displayName = displayName
self.email = email
self.providers = providers
self.providerID = providerID
self.fcmToken = fcmToken
self.accessToken = accessToken
}
}
1 change: 1 addition & 0 deletions Application/Infra/Sources/Common/InfraLayerError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ enum TokenError: Error {

enum SocialLoginError: Error {
case invalidOAuthState
case invalidOAuthCallback
case failedToStartWebAuthenticationSession
case authenticationAlreadyInProgress
}
Expand Down
8 changes: 2 additions & 6 deletions Application/Infra/Sources/Extension/FirebaseAuthUser+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,13 @@ import FirebaseAuth
import Data

extension FirebaseAuth.User {
func makeResponse(
providerID: AuthProviderID,
accessToken: String? = nil
) -> AuthDataResponse {
func makeResponse(providerID: AuthProviderID) -> AuthDataResponse {
return AuthDataResponse(
uid: self.uid,
displayName: self.displayName,
email: self.email,
providers: self.providerData.map { $0.providerID },
providerID: providerID.rawValue,
accessToken: accessToken
providerID: providerID.rawValue
)
}
}
Expand Down
29 changes: 20 additions & 9 deletions Application/Infra/Sources/Service/FunctionAPIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,24 +99,28 @@ struct FunctionAPIEndpoint<Response: Decodable>: NXEndpoint {
let path: String
}

struct GithubAuthenticationResponse: Decodable {
let accessToken: String?
let customToken: String?
struct OAuthAuthenticationSessionRequest: Encodable {
let appChallenge: String
}

struct OAuthAuthenticationSessionResponse: Decodable {
let authorizationURL: URL
}

struct OAuthAuthenticationTicketRequest: Encodable {
let ticket: String
let appVerifier: String
}

struct AppleChallengeResponse: Decodable {
let challengeId: String
let hashedNonce: String
}

struct AppleCustomTokenResponse: Decodable {
struct FirebaseCustomTokenResponse: Decodable {
let customToken: String
}

struct AppleOperationResponse: Decodable {
let success: Bool
}

struct AppleCustomTokenRequest: Encodable {
let challengeId: String
let authorizationCode: String
Expand Down Expand Up @@ -145,6 +149,9 @@ private enum FunctionAPIErrorCode: String {

enum AppleAuthenticationAPIError: Error, Equatable {
case providerLinkConflict
}

enum AuthenticationAPIError: Error, Equatable {
case lastProvider
}

Expand All @@ -169,7 +176,7 @@ struct FunctionAPIServerErrorDecoder: NXServerErrorDecoder {
case .appleProviderLinkConflict:
return AppleAuthenticationAPIError.providerLinkConflict
case .lastProvider:
return AppleAuthenticationAPIError.lastProvider
return AuthenticationAPIError.lastProvider
}
}
}
Expand All @@ -193,6 +200,10 @@ extension Error {
functionAPIUnderlyingError as? AppleAuthenticationAPIError
}

var apiAuthenticationError: AuthenticationAPIError? {
functionAPIUnderlyingError as? AuthenticationAPIError
}

private var functionAPIUnderlyingError: (any Error)? {
guard let error = self as? NXError,
case let .server(statusCode: _, data: _, underlying: underlying) = error else {
Expand Down
26 changes: 16 additions & 10 deletions Application/Infra/Sources/Service/FunctionAPIEndpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,31 @@ extension FunctionAPIEndpoint where Response == EmptyAPIResponse {
}

static let revokeGithubAccessToken = Self(method: .delete, path: "/auth/github/access-token")
static let linkGithubAccount = Self(method: .put, path: "/auth/github/account-link")
static let unlinkGithubAccount = Self(method: .delete, path: "/auth/github/account-link")
static let linkAppleAccount = Self(method: .put, path: "/auth/apple/account-link")
static let unlinkAppleAccount = Self(method: .delete, path: "/auth/apple/account-link")
static let revokeAppleAccessToken = Self(method: .delete, path: "/auth/apple/access-token")
}

extension FunctionAPIEndpoint where Response == AppleChallengeResponse {
static let requestAppleChallenge = Self(method: .post, path: "/auth/apple/challenges")
}

extension FunctionAPIEndpoint where Response == AppleCustomTokenResponse {
extension FunctionAPIEndpoint where Response == FirebaseCustomTokenResponse {
static let requestAppleCustomToken = Self(method: .post, path: "/auth/apple/custom-token")
static let requestGithubCustomToken = Self(method: .post, path: "/auth/github/custom-token")
}

extension FunctionAPIEndpoint where Response == AppleOperationResponse {
static let linkAppleAccount = Self(method: .put, path: "/auth/apple/account-link")
static let unlinkAppleAccount = Self(method: .delete, path: "/auth/apple/account-link")
static let revokeAppleAccessToken = Self(method: .delete, path: "/auth/apple/access-token")
}

extension FunctionAPIEndpoint where Response == GithubAuthenticationResponse {
static let linkGithubProvider = Self(method: .post, path: "/auth/github/link")
static let requestGithubTokens = Self(method: .post, path: "/auth/github/tokens")
extension FunctionAPIEndpoint where Response == OAuthAuthenticationSessionResponse {
static let requestGithubSignInSession = Self(
method: .post,
path: "/auth/github/sign-in-sessions"
)
static let requestGithubAccountLinkSession = Self(
method: .post,
path: "/auth/github/account-link-sessions"
)
}

private func functionAPIPathSegment(_ value: String) -> String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@ final class AppleAuthenticationServiceImpl: AuthenticationService {
func deleteAuth(_ uid: String) async throws {
do {
logger.info("Deleting Apple grant for user: \(uid)")
let response = try await FunctionAPIClient.shared.send(.revokeAppleAccessToken)
try validate(response)
try await FunctionAPIClient.shared.send(.revokeAppleAccessToken)
} catch {
logger.error("Failed to delete Apple auth", error: error)
record(error, code: .deleteAuth)
Expand All @@ -125,15 +124,14 @@ final class AppleAuthenticationServiceImpl: AuthenticationService {
let challenge = try await requestAppleChallenge()
let response = try await authenticateWithAppleAsync(hashedNonce: challenge.hashedNonce)

let operation = try await FunctionAPIClient.shared.send(
try await FunctionAPIClient.shared.send(
.linkAppleAccount,
payload: AppleAccountLinkRequest(
challengeId: challenge.challengeId,
authorizationCode: response.authorizationCode,
credentialEmail: response.email
)
)
try validate(operation)
try await user?.reload()
return true
} catch {
Expand All @@ -149,8 +147,7 @@ final class AppleAuthenticationServiceImpl: AuthenticationService {
func unlink(_ uid: String) async throws {
do {
logger.info("Unlinking Apple account for user: \(uid)")
let response = try await FunctionAPIClient.shared.send(.unlinkAppleAccount)
try validate(response)
try await FunctionAPIClient.shared.send(.unlinkAppleAccount)
try await user?.reload()
} catch {
let mappedError = mapAppleAPIError(error)
Expand Down Expand Up @@ -243,22 +240,18 @@ final class AppleAuthenticationServiceImpl: AuthenticationService {
}

private extension AppleAuthenticationServiceImpl {
func validate(_ response: AppleOperationResponse) throws {
guard response.success else {
throw TokenError.invalidResponse
}
}

func mapAppleAPIError(_ error: Error) -> Error {
if let emailError = error.apiEmailError {
return emailError
}

if error.apiAuthenticationError == .lastProvider {
return DataLayerError.failedToUnlinkLastProvider
}

switch error.apiAppleAuthenticationError {
case .providerLinkConflict:
return DataLayerError.linkCredentialAlreadyInUse
case .lastProvider:
return DataLayerError.failedToUnlinkLastProvider
case .none:
return error
}
Expand Down
Loading
Loading