diff --git a/KkuMulKum.xcodeproj/project.pbxproj b/KkuMulKum.xcodeproj/project.pbxproj index c9e0d2c2..a82c9e87 100644 --- a/KkuMulKum.xcodeproj/project.pbxproj +++ b/KkuMulKum.xcodeproj/project.pbxproj @@ -66,6 +66,7 @@ 78BD612B2C4550A6005752FD /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78BD612A2C4550A6005752FD /* Bundle.swift */; }; 78BD612F2C4561B9005752FD /* KeychainAccessible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78BD612E2C4561B9005752FD /* KeychainAccessible.swift */; }; 78BD61312C456799005752FD /* KeychainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78BD61302C456799005752FD /* KeychainService.swift */; }; + 78BD61342C45B4A7005752FD /* AuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78BD61332C45B4A7005752FD /* AuthService.swift */; }; A3DD9C3D2C41BAD000E58A13 /* MeetingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3DD9C322C41BAD000E58A13 /* MeetingTableViewCell.swift */; }; A3DD9C3E2C41BAD000E58A13 /* MeetingDummyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3DD9C342C41BAD000E58A13 /* MeetingDummyModel.swift */; }; A3DD9C3F2C41BAD000E58A13 /* MeetingListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3DD9C362C41BAD000E58A13 /* MeetingListView.swift */; }; @@ -214,6 +215,7 @@ 78BD612A2C4550A6005752FD /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = ""; }; 78BD612E2C4561B9005752FD /* KeychainAccessible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainAccessible.swift; sourceTree = ""; }; 78BD61302C456799005752FD /* KeychainService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainService.swift; sourceTree = ""; }; + 78BD61332C45B4A7005752FD /* AuthService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthService.swift; sourceTree = ""; }; A3DD9C322C41BAD000E58A13 /* MeetingTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MeetingTableViewCell.swift; sourceTree = ""; }; A3DD9C342C41BAD000E58A13 /* MeetingDummyModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MeetingDummyModel.swift; sourceTree = ""; }; A3DD9C362C41BAD000E58A13 /* MeetingListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MeetingListView.swift; sourceTree = ""; }; @@ -561,6 +563,14 @@ path = KeyChain; sourceTree = ""; }; + 78BD61322C45B4A7005752FD /* Service */ = { + isa = PBXGroup; + children = ( + 78BD61332C45B4A7005752FD /* AuthService.swift */, + ); + path = Service; + sourceTree = ""; + }; A3DD9C332C41BAD000E58A13 /* Cell */ = { isa = PBXGroup; children = ( @@ -969,6 +979,7 @@ DD865B662C39210E00C351A2 /* Login */ = { isa = PBXGroup; children = ( + 78BD61322C45B4A7005752FD /* Service */, 789873372C3D1B4800435E96 /* ViewController */, 789873362C3D1B3900435E96 /* VIewModel */, 789873352C3D1B3000435E96 /* View */, @@ -1560,6 +1571,7 @@ 782B40722C3DBFA3008B0CA7 /* ProfileSetupViewModel.swift in Sources */, DDAF1C8F2C3D6E3D008A37D3 /* PagePromiseSegmentedControl.swift in Sources */, DD49099C2C441719003ED304 /* TardyService.swift in Sources */, + 78BD61342C45B4A7005752FD /* AuthService.swift in Sources */, 782B40722C3DBFA3008B0CA7 /* ProfileSetupViewModel.swift in Sources */, DDAF1C8F2C3D6E3D008A37D3 /* PagePromiseSegmentedControl.swift in Sources */, DE32D1D22C3BF703006848DF /* LoginUserResponseModel.swift in Sources */, diff --git a/KkuMulKum/Application/AppDelegate.swift b/KkuMulKum/Application/AppDelegate.swift index 1ee65ae7..e4c9b001 100644 --- a/KkuMulKum/Application/AppDelegate.swift +++ b/KkuMulKum/Application/AppDelegate.swift @@ -26,6 +26,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { print("Failed to load KAKAO_APP_KEY from PrivacyInfo.plist") } + return true } diff --git a/KkuMulKum/Resource/KeyChain/KeychainService.swift b/KkuMulKum/Resource/KeyChain/KeychainService.swift index 52f052f9..ddbf57c1 100644 --- a/KkuMulKum/Resource/KeyChain/KeychainService.swift +++ b/KkuMulKum/Resource/KeyChain/KeychainService.swift @@ -6,131 +6,105 @@ // import Foundation -import SwiftKeychainWrapper -import Security protocol KeychainService { var accessToken: String? { get set } var refreshToken: String? { get set } - func removeAllTokens() - func verifyKeychainAccess() + var accessTokenExpiresIn: Int? { get set } } class DefaultKeychainService: KeychainService { static let shared = DefaultKeychainService() + private init() {} - private let keychain: KeychainWrapper + private let keychainAccess = DefaultKeychainAccessible() - private struct Key { + struct Key { + static let tempAccessToken = "tempAccessToken" + static let tempRefreshToken = "tempRefreshToken" + static let tempAccessTokenExpiresIn = "tempAccessTokenExpiresIn" + static let accessToken = "accessToken" static let refreshToken = "refreshToken" + static let accessTokenExpiresIn = "accessTokenExpiresIn" } - init() { - let serviceName = Bundle.main.privacyInfo?["ServiceName"] as? String ?? Bundle.main.bundleIdentifier ?? "DefaultServiceName" - self.keychain = KeychainWrapper(serviceName: serviceName) - print("Keychain initialized with service name: \(serviceName)") + var tempAccessToken: String? { + get { + keychainAccess.getToken(Key.tempAccessToken) + } + set { + if newValue != nil { + keychainAccess.saveToken(Key.tempAccessToken, newValue ?? "") + } else { + keychainAccess.remove(Key.tempAccessToken) + } + } } - var accessToken: String? { + var tempRefreshToken: String? { get { - let token = keychain.string(forKey: Key.accessToken) - print("Reading Access Token: \(token ?? "nil")") - return token + keychainAccess.getToken(Key.tempRefreshToken) } set { - if let newValue = newValue { - let success = keychain.set(newValue, forKey: Key.accessToken) - print("Setting Access Token: \(newValue)") - if success { - print("Access Token saved successfully") - // 저장 후 즉시 읽어 확인 - if let savedToken = keychain.string(forKey: Key.accessToken) { - print("Verified Access Token: \(savedToken)") - } else { - print("Failed to verify Access Token after saving") - } - } else { - print("Failed to save Access Token") - printKeychainError(forKey: Key.accessToken) - } + if newValue != nil { + keychainAccess.saveToken(Key.tempRefreshToken, newValue ?? "") } else { - let success = keychain.removeObject(forKey: Key.accessToken) - print(success ? "Access Token removed successfully" : "Failed to remove Access Token") + keychainAccess.remove(Key.tempRefreshToken) } } } - var refreshToken: String? { + var tempAccessTokenExpiresIn: Int? { get { - let token = keychain.string(forKey: Key.refreshToken) - print("Reading Refresh Token: \(token ?? "nil")") - return token + keychainAccess.getExpiresIn(Key.tempAccessTokenExpiresIn) } set { - if let newValue = newValue { - let success = keychain.set(newValue, forKey: Key.refreshToken) - print("Setting Refresh Token: \(newValue)") - if success { - print("Refresh Token saved successfully") - // 저장 후 즉시 읽어 확인 - if let savedToken = keychain.string(forKey: Key.refreshToken) { - print("Verified Refresh Token: \(savedToken)") - } else { - print("Failed to verify Refresh Token after saving") - } - } else { - print("Failed to save Refresh Token") - printKeychainError(forKey: Key.refreshToken) - } + if newValue != nil { + keychainAccess.saveExpiresIn(Key.tempAccessTokenExpiresIn, newValue ?? 0) } else { - let success = keychain.removeObject(forKey: Key.refreshToken) - print(success ? "Refresh Token removed successfully" : "Failed to remove Refresh Token") + keychainAccess.remove(Key.tempAccessTokenExpiresIn) } } } - func removeAllTokens() { - keychain.removeObject(forKey: Key.accessToken) - keychain.removeObject(forKey: Key.refreshToken) - print("All tokens removed from keychain") + + var accessToken: String? { + get { + keychainAccess.getToken(Key.accessToken) + } + set { + if newValue != nil { + keychainAccess.saveToken(Key.accessToken, newValue ?? "") + } else { + keychainAccess.remove(Key.accessToken) + } + } } - func verifyKeychainAccess() { - let testKey = "TestKeychainAccess" - let testValue = "TestValue" - - // 키체인에 테스트 값 저장 - let saveSuccess = keychain.set(testValue, forKey: testKey) - if saveSuccess { - print("Test value saved to keychain successfully") - - // 저장된 값 읽기 - if let retrievedValue = keychain.string(forKey: testKey) { - print("Test value retrieved successfully: \(retrievedValue)") + var refreshToken: String? { + get { + keychainAccess.getToken(Key.refreshToken) + } + set { + if newValue != nil { + keychainAccess.saveToken(Key.refreshToken, newValue ?? "") } else { - print("Failed to retrieve test value") + keychainAccess.remove(Key.refreshToken) } - - // 테스트 값 삭제 - let removeSuccess = keychain.removeObject(forKey: testKey) - print(removeSuccess ? "Test value removed successfully" : "Failed to remove test value") - } else { - print("Failed to save test value to keychain") - printKeychainError(forKey: testKey) } } - private func printKeychainError(forKey key: String) { - let query: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrAccount as String: key, - kSecAttrService as String: keychain.serviceName, - kSecReturnData as String: true - ] - - var item: CFTypeRef? - let status = SecItemCopyMatching(query as CFDictionary, &item) - print("Keychain error for key '\(key)': \(SecCopyErrorMessageString(status, nil) ?? "Unknown error" as CFString)") + var accessTokenExpiresIn: Int? { + get { + keychainAccess.getExpiresIn(Key.accessTokenExpiresIn) + } + set { + if newValue != nil { + keychainAccess.saveExpiresIn(Key.accessTokenExpiresIn, newValue ?? 0) + } else { + keychainAccess.remove(Key.accessTokenExpiresIn) + } + } } } diff --git a/KkuMulKum/Source/Onboarding/Login/VIewModel/LoginViewModel.swift b/KkuMulKum/Source/Onboarding/Login/VIewModel/LoginViewModel.swift index 4f4160d1..658639ef 100644 --- a/KkuMulKum/Source/Onboarding/Login/VIewModel/LoginViewModel.swift +++ b/KkuMulKum/Source/Onboarding/Login/VIewModel/LoginViewModel.swift @@ -99,51 +99,38 @@ class LoginViewModel: NSObject { } private func handleLoginResponse(_ response: ResponseBodyDTO) { - print("Handling login response") - if response.success { - if let data = response.data { - if data.name != nil { - print("Login successful") - loginState.value = .login - } else { - print("Login successful, but needs onboarding.") - loginState.value = .needOnboarding - } - - saveTokens(accessToken: data.jwtTokenDTO.accessToken, refreshToken: data.jwtTokenDTO.refreshToken) + print("Handling login response") + if response.success { + if let data = response.data { + if data.name != nil { + print("Login successful") + loginState.value = .login } else { - print("Warning: No data received in response") - error.value = "No data received" + print("Login successful, but needs onboarding.") + loginState.value = .needOnboarding } + + saveTokens(accessToken: data.jwtTokenDTO.accessToken, refreshToken: data.jwtTokenDTO.refreshToken) } else { - if let error = response.error { - print("Login failed: \(error.message)") - self.error.value = error.message - } else { - print("Login failed: Unknown error") - self.error.value = "Unknown error occurred" - } + print("Warning: No data received in response") + error.value = "No data received" } - } - - private func saveTokens(accessToken: String, refreshToken: String) { - keychainService.accessToken = accessToken - keychainService.refreshToken = refreshToken - print("Tokens saved to keychain") - - // 저장 후 바로 읽어서 확인 - if let savedAccessToken = keychainService.accessToken { - print("Saved Access Token: \(savedAccessToken)") } else { - print("Failed to save Access Token") + if let error = response.error { + print("Login failed: \(error.message)") + self.error.value = error.message + } else { + print("Login failed: Unknown error") + self.error.value = "Unknown error occurred" + } } + } - if let savedRefreshToken = keychainService.refreshToken { - print("Saved Refresh Token: \(savedRefreshToken)") - } else { - print("Failed to save Refresh Token") + private func saveTokens(accessToken: String, refreshToken: String) { + keychainService.accessToken = accessToken + keychainService.refreshToken = refreshToken + print("Tokens saved to keychain") } - } func refreshToken() { guard let refreshToken = keychainService.refreshToken else {