-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathAuthRepository.swift
More file actions
177 lines (152 loc) · 6.5 KB
/
Copy pathAuthRepository.swift
File metadata and controls
177 lines (152 loc) · 6.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
//
// AuthRepository.swift
// DataSource
//
// Created by 최정인 on 6/30/25.
//
import Foundation
import Domain
import Shared
import KakaoSDKUser
import KakaoSDKAuth
final class AuthRepository: AuthRepositoryProtocol {
private let networkService: NetworkServiceProtocol
private let keychainStorage: KeychainStorageProtocol
private let userDefaultsStorage: UserDefaultsStorageProtocol
init(
networkService: NetworkServiceProtocol,
keychainStorage: KeychainStorageProtocol,
userDefaultsStorage: UserDefaultsStorageProtocol
) {
self.networkService = networkService
self.keychainStorage = keychainStorage
self.userDefaultsStorage = userDefaultsStorage
}
func kakaoLogin() async throws -> UserEntity {
let accessToken = try await fetchKakaoToken()
let user = try await requestServerLogin(
socialType: .kakao,
nickname: nil,
token: accessToken)
return user
}
func appleLogin(nickname: String?, authToken: String) async throws -> UserEntity {
var savedNickname: String = ""
if let nickname {
try saveNickname(nickname: nickname)
savedNickname = nickname
} else {
savedNickname = try loadNickname()
}
let user = try await requestServerLogin(
socialType: .apple,
nickname: savedNickname,
token: authToken)
return user
}
func submitAgreement(agreements: [TermsType : Bool]) async throws {
let accessToken = try loadToken(tokenType: .accessToken)
let endpoint = AuthEndpoint.agreements(accessToken: accessToken, agreements: agreements)
_ = try await networkService.request(endpoint: endpoint, type: EmptyResponseDTO.self)
}
func logout() async throws {
let accessToken = try loadToken(tokenType: .accessToken)
let endpoint = AuthEndpoint.logout(accessToken: accessToken)
_ = try await networkService.request(endpoint: endpoint, type: String.self)
try removeToken()
}
func withdraw() async throws {
let accessToken = try loadToken(tokenType: .accessToken)
let endpoint = AuthEndpoint.withdraw(accessToken: accessToken)
_ = try await networkService.request(endpoint: endpoint, type: String.self)
try removeToken()
try removeNickname()
}
func reissueToken() async throws {
let refreshToken = try loadToken(tokenType: .refreshToken)
let endpoint = AuthEndpoint.reissue(refreshToken: refreshToken)
guard let userResponse = try await networkService.request(endpoint: endpoint, type: LoginResponseDTO.self)
else { return }
let userEntity = userResponse.toUserEntity()
guard
saveToken(tokenType: .accessToken, token: userEntity.accessToken),
saveToken(tokenType: .refreshToken, token: userEntity.refreshToken)
else { throw AuthError.tokenSaveFailed }
BitnagilLogger.log(logType: .debug, message: "User Logined: \(userEntity.userState)")
BitnagilLogger.log(logType: .debug, message: "AccessToken Saved: \(userEntity.accessToken)")
BitnagilLogger.log(logType: .debug, message: "RefreshToken Saved: \(userEntity.refreshToken)")
}
private func fetchKakaoToken() async throws -> String {
try await withCheckedThrowingContinuation { continuation in
let resultHandler: (OAuthToken?, Error?) -> Void = { oauthToken, error in
if let error {
continuation.resume(throwing: AuthError.unknown(error))
} else if let oauthToken {
continuation.resume(returning: oauthToken.accessToken)
} else {
continuation.resume(throwing: AuthError.kakaoTokenFetchFailed)
}
}
Task { @MainActor in
if UserApi.isKakaoTalkLoginAvailable() {
UserApi.shared.loginWithKakaoTalk(completion: resultHandler)
} else {
UserApi.shared.loginWithKakaoAccount(completion: resultHandler)
}
}
}
}
private func requestServerLogin(
socialType: SocialLoginType,
nickname: String?,
token: String
) async throws -> UserEntity {
let endpoint = AuthEndpoint.login(
socialLoginType: socialType,
nickname: nickname,
token: token)
guard let userResponse = try await networkService.request(endpoint: endpoint, type: LoginResponseDTO.self)
else { throw AuthError.invalidUserData }
let userEntity = userResponse.toUserEntity()
guard
saveToken(tokenType: .accessToken, token: userEntity.accessToken),
saveToken(tokenType: .refreshToken, token: userEntity.refreshToken)
else { throw AuthError.tokenSaveFailed }
BitnagilLogger.log(logType: .debug, message: "User Logined: \(userEntity.userState)")
BitnagilLogger.log(logType: .debug, message: "AccessToken Saved: \(userEntity.accessToken)")
BitnagilLogger.log(logType: .debug, message: "RefreshToken Saved: \(userEntity.refreshToken)")
return userEntity
}
private func saveToken(tokenType: TokenType, token: String) -> Bool {
return keychainStorage.save(token, forKey: tokenType.rawValue)
}
private func loadToken(tokenType: TokenType) throws -> String {
guard let token = keychainStorage.load(forKey: tokenType.rawValue) else {
throw AuthError.tokenLoadFailed
}
return token
}
private func removeToken() throws {
guard
keychainStorage.remove(forKey: TokenType.accessToken.rawValue),
keychainStorage.remove(forKey: TokenType.refreshToken.rawValue)
else { throw AuthError.tokenRemoveFailed }
}
private func saveNickname(nickname: String) throws {
guard userDefaultsStorage.save(nickname, forKey: UserDefaultsKey.nickname.rawValue) else {
throw AuthError.nicknameSaveFailed
}
}
private func loadNickname() throws -> String {
let nickname: String? = userDefaultsStorage.load(forKey: UserDefaultsKey.nickname.rawValue)
guard let nickname else {
throw AuthError.nicknameLoadFailed
}
return nickname
}
private func removeNickname() throws {
guard userDefaultsStorage.remove(forKey: UserDefaultsKey.nickname.rawValue) else {
throw AuthError.nicknameRemoveFailed
}
}
}