Skip to content

Commit 66e1d7e

Browse files
committed
refactor: GitHub 인증을 서버 callback 흐름으로 전환
1 parent aa07901 commit 66e1d7e

9 files changed

Lines changed: 386 additions & 288 deletions

Application/Infra/Sources/Common/InfraLayerError.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ enum TokenError: Error {
3030

3131
enum SocialLoginError: Error {
3232
case invalidOAuthState
33+
case invalidOAuthCallback
3334
case failedToStartWebAuthenticationSession
3435
case authenticationAlreadyInProgress
3536
}

Application/Infra/Sources/Service/FunctionAPIClient.swift

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,24 +99,28 @@ struct FunctionAPIEndpoint<Response: Decodable>: NXEndpoint {
9999
let path: String
100100
}
101101

102-
struct GithubAuthenticationResponse: Decodable {
103-
let accessToken: String?
104-
let customToken: String?
102+
struct OAuthAuthenticationSessionRequest: Encodable {
103+
let appChallenge: String
104+
}
105+
106+
struct OAuthAuthenticationSessionResponse: Decodable {
107+
let authorizationURL: URL
108+
}
109+
110+
struct OAuthAuthenticationTicketRequest: Encodable {
111+
let ticket: String
112+
let appVerifier: String
105113
}
106114

107115
struct AppleChallengeResponse: Decodable {
108116
let challengeId: String
109117
let hashedNonce: String
110118
}
111119

112-
struct AppleCustomTokenResponse: Decodable {
120+
struct FirebaseCustomTokenResponse: Decodable {
113121
let customToken: String
114122
}
115123

116-
struct AppleOperationResponse: Decodable {
117-
let success: Bool
118-
}
119-
120124
struct AppleCustomTokenRequest: Encodable {
121125
let challengeId: String
122126
let authorizationCode: String
@@ -145,6 +149,9 @@ private enum FunctionAPIErrorCode: String {
145149

146150
enum AppleAuthenticationAPIError: Error, Equatable {
147151
case providerLinkConflict
152+
}
153+
154+
enum AuthenticationAPIError: Error, Equatable {
148155
case lastProvider
149156
}
150157

@@ -169,7 +176,7 @@ struct FunctionAPIServerErrorDecoder: NXServerErrorDecoder {
169176
case .appleProviderLinkConflict:
170177
return AppleAuthenticationAPIError.providerLinkConflict
171178
case .lastProvider:
172-
return AppleAuthenticationAPIError.lastProvider
179+
return AuthenticationAPIError.lastProvider
173180
}
174181
}
175182
}
@@ -193,6 +200,10 @@ extension Error {
193200
functionAPIUnderlyingError as? AppleAuthenticationAPIError
194201
}
195202

203+
var apiAuthenticationError: AuthenticationAPIError? {
204+
functionAPIUnderlyingError as? AuthenticationAPIError
205+
}
206+
196207
private var functionAPIUnderlyingError: (any Error)? {
197208
guard let error = self as? NXError,
198209
case let .server(statusCode: _, data: _, underlying: underlying) = error else {

Application/Infra/Sources/Service/FunctionAPIEndpoint.swift

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,31 @@ extension FunctionAPIEndpoint where Response == EmptyAPIResponse {
3535
}
3636

3737
static let revokeGithubAccessToken = Self(method: .delete, path: "/auth/github/access-token")
38+
static let linkGithubAccount = Self(method: .put, path: "/auth/github/account-link")
39+
static let unlinkGithubAccount = Self(method: .delete, path: "/auth/github/account-link")
40+
static let linkAppleAccount = Self(method: .put, path: "/auth/apple/account-link")
41+
static let unlinkAppleAccount = Self(method: .delete, path: "/auth/apple/account-link")
42+
static let revokeAppleAccessToken = Self(method: .delete, path: "/auth/apple/access-token")
3843
}
3944

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

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

48-
extension FunctionAPIEndpoint where Response == AppleOperationResponse {
49-
static let linkAppleAccount = Self(method: .put, path: "/auth/apple/account-link")
50-
static let unlinkAppleAccount = Self(method: .delete, path: "/auth/apple/account-link")
51-
static let revokeAppleAccessToken = Self(method: .delete, path: "/auth/apple/access-token")
52-
}
53-
54-
extension FunctionAPIEndpoint where Response == GithubAuthenticationResponse {
55-
static let linkGithubProvider = Self(method: .post, path: "/auth/github/link")
56-
static let requestGithubTokens = Self(method: .post, path: "/auth/github/tokens")
54+
extension FunctionAPIEndpoint where Response == OAuthAuthenticationSessionResponse {
55+
static let requestGithubSignInSession = Self(
56+
method: .post,
57+
path: "/auth/github/sign-in-sessions"
58+
)
59+
static let requestGithubAccountLinkSession = Self(
60+
method: .post,
61+
path: "/auth/github/account-link-sessions"
62+
)
5763
}
5864

5965
private func functionAPIPathSegment(_ value: String) -> String {

Application/Infra/Sources/Service/SocialLogin/AppleAuthenticationServiceImpl.swift

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,7 @@ final class AppleAuthenticationServiceImpl: AuthenticationService {
110110
func deleteAuth(_ uid: String) async throws {
111111
do {
112112
logger.info("Deleting Apple grant for user: \(uid)")
113-
let response = try await FunctionAPIClient.shared.send(.revokeAppleAccessToken)
114-
try validate(response)
113+
try await FunctionAPIClient.shared.send(.revokeAppleAccessToken)
115114
} catch {
116115
logger.error("Failed to delete Apple auth", error: error)
117116
record(error, code: .deleteAuth)
@@ -125,15 +124,14 @@ final class AppleAuthenticationServiceImpl: AuthenticationService {
125124
let challenge = try await requestAppleChallenge()
126125
let response = try await authenticateWithAppleAsync(hashedNonce: challenge.hashedNonce)
127126

128-
let operation = try await FunctionAPIClient.shared.send(
127+
try await FunctionAPIClient.shared.send(
129128
.linkAppleAccount,
130129
payload: AppleAccountLinkRequest(
131130
challengeId: challenge.challengeId,
132131
authorizationCode: response.authorizationCode,
133132
credentialEmail: response.email
134133
)
135134
)
136-
try validate(operation)
137135
try await user?.reload()
138136
return true
139137
} catch {
@@ -149,8 +147,7 @@ final class AppleAuthenticationServiceImpl: AuthenticationService {
149147
func unlink(_ uid: String) async throws {
150148
do {
151149
logger.info("Unlinking Apple account for user: \(uid)")
152-
let response = try await FunctionAPIClient.shared.send(.unlinkAppleAccount)
153-
try validate(response)
150+
try await FunctionAPIClient.shared.send(.unlinkAppleAccount)
154151
try await user?.reload()
155152
} catch {
156153
let mappedError = mapAppleAPIError(error)
@@ -243,22 +240,18 @@ final class AppleAuthenticationServiceImpl: AuthenticationService {
243240
}
244241

245242
private extension AppleAuthenticationServiceImpl {
246-
func validate(_ response: AppleOperationResponse) throws {
247-
guard response.success else {
248-
throw TokenError.invalidResponse
249-
}
250-
}
251-
252243
func mapAppleAPIError(_ error: Error) -> Error {
253244
if let emailError = error.apiEmailError {
254245
return emailError
255246
}
256247

248+
if error.apiAuthenticationError == .lastProvider {
249+
return DataLayerError.failedToUnlinkLastProvider
250+
}
251+
257252
switch error.apiAppleAuthenticationError {
258253
case .providerLinkConflict:
259254
return DataLayerError.linkCredentialAlreadyInUse
260-
case .lastProvider:
261-
return DataLayerError.failedToUnlinkLastProvider
262255
case .none:
263256
return error
264257
}

0 commit comments

Comments
 (0)