From 62029e4cf751e432504b48ee28c6cea73addbcbc Mon Sep 17 00:00:00 2001 From: Seokki-Kwon Date: Mon, 11 Nov 2024 15:10:25 +0900 Subject: [PATCH 1/9] =?UTF-8?q?fix:=20=EC=95=A0=ED=94=8C=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EB=B0=98=ED=99=98=EA=B0=92=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xcshareddata/swiftpm/Package.resolved | 3 ++- .../UserDataInteractor+Migration.swift | 4 +++- .../Domain/Interactors/UserDataInteractor.swift | 5 ++++- .../Util/Services/AppleLoginManager.swift | 16 ++++++---------- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/HongikYeolgong2.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/HongikYeolgong2.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 2ed1af8..6880c6f 100644 --- a/HongikYeolgong2.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/HongikYeolgong2.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,4 +1,5 @@ { + "originHash" : "a1569f9895aa2be8e24832f98525d5da4eb90b5d158a82691c15b47eb72a13d7", "pins" : [ { "identity" : "abseil-cpp-binary", @@ -118,5 +119,5 @@ } } ], - "version" : 2 + "version" : 3 } diff --git a/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift b/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift index 703b31e..bf15665 100644 --- a/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift +++ b/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift @@ -33,7 +33,9 @@ final class UserDataMigrationInteractor: UserDataInteractor { /// 애플로그인을 요청합니다. /// - Parameter authorization: ASAuthorization func requestAppleLogin(_ authorization: ASAuthorization) { - guard let (email, idToken) = authService.requestAppleLogin(authorization) else { + guard let appleIDCredential = authService.requestAppleLogin(authorization), + let idTokenData = appleIDCredential.identityToken, + let idToken = String(data: idTokenData, encoding: .utf8) else { return } diff --git a/HongikYeolgong2/Domain/Interactors/UserDataInteractor.swift b/HongikYeolgong2/Domain/Interactors/UserDataInteractor.swift index e28069b..4548e92 100644 --- a/HongikYeolgong2/Domain/Interactors/UserDataInteractor.swift +++ b/HongikYeolgong2/Domain/Interactors/UserDataInteractor.swift @@ -39,7 +39,10 @@ final class UserDataInteractorImpl: UserDataInteractor { /// 애플로그인을 요청합니다. /// - Parameter authorization: ASAuthorization func requestAppleLogin(_ authorization: ASAuthorization) { - guard let (email, idToken) = authService.requestAppleLogin(authorization) else { + guard let appleIDCredential = authService.requestAppleLogin(authorization), + let email = appleIDCredential.email, + let idTokenData = appleIDCredential.identityToken, + let idToken = String(data: idTokenData, encoding: .utf8) else { return } diff --git a/HongikYeolgong2/Util/Services/AppleLoginManager.swift b/HongikYeolgong2/Util/Services/AppleLoginManager.swift index a1920fa..901d2f7 100644 --- a/HongikYeolgong2/Util/Services/AppleLoginManager.swift +++ b/HongikYeolgong2/Util/Services/AppleLoginManager.swift @@ -9,23 +9,19 @@ import Foundation import AuthenticationServices protocol AppleLoginManager { - func requestAppleLogin(_ authrization: ASAuthorization) -> (email: String, idToken: String)? + func requestAppleLogin(_ authrization: ASAuthorization) -> ASAuthorizationAppleIDCredential? } final class AuthenticationServiceImpl: AppleLoginManager { - /// 애플로그인 요청을 위한 이메일과 토큰을 반환합니다.(이메일은 첫로그인시 반환) + /// 애플로그인 요청을 위한 AppleIDCredential 을 반환합니다. /// - Parameter authorization: authorization - /// - Returns: email, identityToken - func requestAppleLogin(_ authorization: ASAuthorization) -> (email: String, idToken: String)? { - guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential, - let idTokenData = appleIDCredential.identityToken, - let idToken = String(data: idTokenData, encoding: .utf8) else { + /// - Returns: AppleIDCredential + func requestAppleLogin(_ authorization: ASAuthorization) -> ASAuthorizationAppleIDCredential? { + guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential else { return nil } - let email = appleIDCredential.email ?? "" - - return (email: email, idToken: idToken) + return appleIDCredential } } From 89a930c4babb6d1e41d60e85ba3156b63d11c5ca Mon Sep 17 00:00:00 2001 From: Seokki-Kwon Date: Mon, 11 Nov 2024 17:55:13 +0900 Subject: [PATCH 2/9] =?UTF-8?q?refactor:=20=EC=95=A0=ED=94=8C=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20Delegate=20=EB=B0=A9=EC=8B=9D=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HongikYeolgong2/Core/AppEnviroment.swift | 4 +- .../UserDataInteractor+Migration.swift | 36 ++++++---- .../Interactors/UserDataInteractor.swift | 4 +- .../Injected/DependencyInjector.swift | 2 +- .../Injected/InteractorsContainer.swift | 2 +- .../Util/Services/AppleLoginManager.swift | 67 ++++++++++++++++++- 6 files changed, 92 insertions(+), 23 deletions(-) diff --git a/HongikYeolgong2/Core/AppEnviroment.swift b/HongikYeolgong2/Core/AppEnviroment.swift index eb40da3..86da4b2 100644 --- a/HongikYeolgong2/Core/AppEnviroment.swift +++ b/HongikYeolgong2/Core/AppEnviroment.swift @@ -93,7 +93,7 @@ extension AppEnviroment { /// 앱의 서비스를 구성하는 컨테이너를 반환합니다. /// - Returns: 앱의 서비스를 구성하는 컨테이너 static func configuredServices() -> DIContainer.Services { - .init(appleAuthService: AuthenticationServiceImpl()) + .init(appleAuthService: AppleLoginManager()) } } @@ -105,6 +105,6 @@ extension DIContainer { } struct Services { - let appleAuthService: AppleLoginManager + let appleAuthService: AppleLoginService } } diff --git a/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift b/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift index bf15665..4604bd8 100644 --- a/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift +++ b/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift @@ -19,12 +19,12 @@ final class UserDataMigrationInteractor: UserDataInteractor { private let cancleBag = CancelBag() private let appState: Store private let authRepository: AuthRepository - private let authService: AppleLoginManager + private let authService: AppleLoginService private let db = Firestore.firestore() init(appState: Store, authRepository: AuthRepository, - authService: AppleLoginManager) { + authService: AppleLoginService) { self.appState = appState self.authRepository = authRepository self.authService = authService @@ -185,20 +185,28 @@ final class UserDataMigrationInteractor: UserDataInteractor { } func withdraw() { - authRepository - .withdraw() - .sink(receiveCompletion: { _ in }) { [weak self] in - guard let self = self else { return } - appState.bulkUpdate { appState in - appState.userSession = .unauthenticated - appState.userData = .init() - appState.permissions = .init() - appState.studySession = .init() - appState.system = .init() - } - KeyChainManager.deleteItem(key: .accessToken) + authService.performExistingAccountSetupFlows() + .sink { _ in + + } receiveValue: { appleIDcredential in + guard let appleIDcredential = appleIDcredential else { return } } .store(in: cancleBag) + +// authRepository +// .withdraw() +// .sink(receiveCompletion: { _ in }) { [weak self] in +// guard let self = self else { return } +// appState.bulkUpdate { appState in +// appState.userSession = .unauthenticated +// appState.userData = .init() +// appState.permissions = .init() +// appState.studySession = .init() +// appState.system = .init() +// } +// KeyChainManager.deleteItem(key: .accessToken) +// } +// .store(in: cancleBag) } } diff --git a/HongikYeolgong2/Domain/Interactors/UserDataInteractor.swift b/HongikYeolgong2/Domain/Interactors/UserDataInteractor.swift index 4548e92..46e527f 100644 --- a/HongikYeolgong2/Domain/Interactors/UserDataInteractor.swift +++ b/HongikYeolgong2/Domain/Interactors/UserDataInteractor.swift @@ -26,11 +26,11 @@ final class UserDataInteractorImpl: UserDataInteractor { private let cancleBag = CancelBag() private let appState: Store private let authRepository: AuthRepository - private let authService: AppleLoginManager + private let authService: AppleLoginService init(appState: Store, authRepository: AuthRepository, - authService: AppleLoginManager) { + authService: AppleLoginService) { self.appState = appState self.authRepository = authRepository self.authService = authService diff --git a/HongikYeolgong2/Injected/DependencyInjector.swift b/HongikYeolgong2/Injected/DependencyInjector.swift index 86c683b..b8255d8 100644 --- a/HongikYeolgong2/Injected/DependencyInjector.swift +++ b/HongikYeolgong2/Injected/DependencyInjector.swift @@ -26,7 +26,7 @@ struct DIContainer: EnvironmentKey { private static let `default` = Self(appState: .init(AppState()), interactors: .default, - services: .init(appleAuthService: AuthenticationServiceImpl())) + services: .init(appleAuthService: AppleLoginManager())) } extension EnvironmentValues { diff --git a/HongikYeolgong2/Injected/InteractorsContainer.swift b/HongikYeolgong2/Injected/InteractorsContainer.swift index 83921c0..84bd6d5 100644 --- a/HongikYeolgong2/Injected/InteractorsContainer.swift +++ b/HongikYeolgong2/Injected/InteractorsContainer.swift @@ -39,7 +39,7 @@ extension DIContainer { userDataInteractor: UserDataInteractorImpl( appState: Store(AppState()), authRepository: AuthRepositoryImpl(), - authService: AuthenticationServiceImpl() + authService: AppleLoginManager() ), studyTimeInteractor: StudyTimeInteractorImpl(studySessionRepository: StudySessionRepositoryImpl()), studySessionInteractor: StudySessionInteractorImpl(appState: Store(AppState()), studySessionRepository: StudySessionRepositoryImpl()), diff --git a/HongikYeolgong2/Util/Services/AppleLoginManager.swift b/HongikYeolgong2/Util/Services/AppleLoginManager.swift index 901d2f7..5ad3031 100644 --- a/HongikYeolgong2/Util/Services/AppleLoginManager.swift +++ b/HongikYeolgong2/Util/Services/AppleLoginManager.swift @@ -5,14 +5,21 @@ // Created by 권석기 on 10/14/24. // -import Foundation +import Combine import AuthenticationServices -protocol AppleLoginManager { +enum AppleLoginError: Error { + case typeError +} + +protocol AppleLoginService { func requestAppleLogin(_ authrization: ASAuthorization) -> ASAuthorizationAppleIDCredential? + func requestAppleLogin() + func performExistingAccountSetupFlows() -> AnyPublisher } -final class AuthenticationServiceImpl: AppleLoginManager { +final class AppleLoginManager: NSObject, AppleLoginService, ASAuthorizationControllerDelegate, ASWebAuthenticationPresentationContextProviding, ASAuthorizationControllerPresentationContextProviding { + private var appleLoginCompletion: ((Result) -> Void)? /// 애플로그인 요청을 위한 AppleIDCredential 을 반환합니다. /// - Parameter authorization: authorization @@ -24,4 +31,58 @@ final class AuthenticationServiceImpl: AppleLoginManager { return appleIDCredential } + + func requestAppleLogin() { + let appleIDProvider = ASAuthorizationAppleIDProvider() + let request = appleIDProvider.createRequest() + request.requestedScopes = [.fullName, .email] + + let authorizationController = ASAuthorizationController(authorizationRequests: [request]) + + authorizationController.delegate = self + authorizationController.presentationContextProvider = self + + authorizationController.performRequests() + } + + func performExistingAccountSetupFlows() -> AnyPublisher { + return Future { [weak self] promise in + guard let self = self else { return } + let requests = [ASAuthorizationAppleIDProvider().createRequest(), + ASAuthorizationPasswordProvider().createRequest()] + + // Create an authorization controller with the given requests. + let authorizationController = ASAuthorizationController(authorizationRequests: requests) + + // (Result) -> Void + + appleLoginCompletion = promise + + authorizationController.delegate = self + authorizationController.presentationContextProvider = self + authorizationController.performRequests() + }.eraseToAnyPublisher() + } + + func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { + + guard let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential else { + appleLoginCompletion?(.failure(AppleLoginError.typeError)) + return + } + + appleLoginCompletion?(.success(appleIDCredential)) + } + + func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { + appleLoginCompletion?(.failure(error)) + } + + func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { + return ASPresentationAnchor() + } + + func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { + return ASPresentationAnchor() + } } From eaa37c142c3f5ac7e1137b87f816dc02ff86b311 Mon Sep 17 00:00:00 2001 From: Seokki-Kwon Date: Mon, 11 Nov 2024 20:34:43 +0900 Subject: [PATCH 3/9] =?UTF-8?q?feat:=20=EC=86=8C=EC=85=9C=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=B2=98=EB=A6=AC=EC=8B=9C=20SocialLoginR?= =?UTF-8?q?epository=20=EB=B3=84=EB=8F=84=EB=A1=9C=20=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HongikYeolgong2.xcodeproj/project.pbxproj | 12 +++++ .../Auth/SocialLoginRepositoryImpl.swift | 20 +++++++ .../UserDataInteractor+Migration.swift | 37 ++++++------- .../Interfaces/SocialLoginRepository.swift | 10 ++++ HongikYeolgong2/Info.plist | 2 + .../Util/API/Endpoints/ASAuthEndpoint.swift | 54 +++++++++++++++++++ HongikYeolgong2/Util/Constants.swift | 1 + 7 files changed, 118 insertions(+), 18 deletions(-) create mode 100644 HongikYeolgong2/Data/Repositories/Auth/SocialLoginRepositoryImpl.swift create mode 100644 HongikYeolgong2/Domain/Interfaces/SocialLoginRepository.swift create mode 100644 HongikYeolgong2/Util/API/Endpoints/ASAuthEndpoint.swift diff --git a/HongikYeolgong2.xcodeproj/project.pbxproj b/HongikYeolgong2.xcodeproj/project.pbxproj index 49c796e..824c18f 100644 --- a/HongikYeolgong2.xcodeproj/project.pbxproj +++ b/HongikYeolgong2.xcodeproj/project.pbxproj @@ -110,6 +110,9 @@ 47BACCF72CA164BA00295DAC /* Font+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47BACCF62CA164BA00295DAC /* Font+.swift */; }; 47BE30E32CC813BB0015D973 /* KeyChainManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47BE30E22CC813BB0015D973 /* KeyChainManager.swift */; }; 47BE30E52CC81A9E0015D973 /* URLRequest+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47BE30E42CC81A9E0015D973 /* URLRequest+.swift */; }; + 47C815382CE21E640017EA24 /* ASAuthEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47C815372CE21E640017EA24 /* ASAuthEndpoint.swift */; }; + 47C815412CE221060017EA24 /* SocialLoginRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47C815402CE221060017EA24 /* SocialLoginRepositoryImpl.swift */; }; + 47C815432CE2211D0017EA24 /* SocialLoginRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47C815422CE2211D0017EA24 /* SocialLoginRepository.swift */; }; 47CA17252CC9336100CBB251 /* StudyPeriodView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47CA17242CC9336100CBB251 /* StudyPeriodView.swift */; }; 47CA17272CC9340800CBB251 /* StudyTimerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47CA17262CC9340800CBB251 /* StudyTimerView.swift */; }; 47D5EDCD2CCBCB6D00ACA469 /* ImageBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D5EDCC2CCBCB6D00ACA469 /* ImageBackground.swift */; }; @@ -275,6 +278,9 @@ 47BACCF62CA164BA00295DAC /* Font+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Font+.swift"; sourceTree = ""; }; 47BE30E22CC813BB0015D973 /* KeyChainManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyChainManager.swift; sourceTree = ""; }; 47BE30E42CC81A9E0015D973 /* URLRequest+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLRequest+.swift"; sourceTree = ""; }; + 47C815372CE21E640017EA24 /* ASAuthEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASAuthEndpoint.swift; sourceTree = ""; }; + 47C815402CE221060017EA24 /* SocialLoginRepositoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialLoginRepositoryImpl.swift; sourceTree = ""; }; + 47C815422CE2211D0017EA24 /* SocialLoginRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialLoginRepository.swift; sourceTree = ""; }; 47CA17242CC9336100CBB251 /* StudyPeriodView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyPeriodView.swift; sourceTree = ""; }; 47CA17262CC9340800CBB251 /* StudyTimerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyTimerView.swift; sourceTree = ""; }; 47D5EDCC2CCBCB6D00ACA469 /* ImageBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageBackground.swift; sourceTree = ""; }; @@ -353,6 +359,7 @@ 47F635012CC3E98D0034EAA9 /* UserEndpoint.swift */, 4786A1062CCA33DF008635A4 /* WeeklyEndpoint.swift */, 470483A72CDB50FA00C381ED /* TokenEndpoint.swift */, + 47C815372CE21E640017EA24 /* ASAuthEndpoint.swift */, ); path = Endpoints; sourceTree = ""; @@ -371,6 +378,7 @@ isa = PBXGroup; children = ( 4707230A2CBC19C10046469F /* AuthRepositoryImpl.swift */, + 47C815402CE221060017EA24 /* SocialLoginRepositoryImpl.swift */, ); path = Auth; sourceTree = ""; @@ -685,6 +693,7 @@ 470723082CBC198E0046469F /* AuthRepository.swift */, 4786A1082CCA346F008635A4 /* StudySessionRepository.swift */, 478F843D2CD326E90097CAA1 /* WeeklyRepository.swift */, + 47C815422CE2211D0017EA24 /* SocialLoginRepository.swift */, ); path = Interfaces; sourceTree = ""; @@ -1134,6 +1143,7 @@ 472319ED2CD1FC32009BA019 /* UIApplication+.swift in Sources */, 4763FFB12CB90C1500990336 /* DependencyInjector.swift in Sources */, 47F79B2E2CCCB7AA00DD0899 /* TimePickerView.swift in Sources */, + 47C815432CE2211D0017EA24 /* SocialLoginRepository.swift in Sources */, 476D26092CDDF2B6002FEF5C /* CalendarCountAllResponseDTO.swift in Sources */, 478F84392CD3176E0097CAA1 /* RankingDataInteractor.swift in Sources */, 475B86E12CA1AF31000534B2 /* RankingView.swift in Sources */, @@ -1147,6 +1157,7 @@ 478F84442CD350850097CAA1 /* WeeklyRankingResponseDTO.swift in Sources */, 473E8EBC2CCEBEF3000F102C /* ModalView.swift in Sources */, 473E8EB62CCE6A02000F102C /* Date+.swift in Sources */, + 47C815412CE221060017EA24 /* SocialLoginRepositoryImpl.swift in Sources */, 470483A82CDB50FA00C381ED /* TokenEndpoint.swift in Sources */, 471940CB2CAFD9B700426D30 /* RankingCell.swift in Sources */, 473671A72CB1404E00527896 /* Ratio.swift in Sources */, @@ -1170,6 +1181,7 @@ 478F84412CD33F620097CAA1 /* WeekFieldResponseDTO.swift in Sources */, 473E8EBA2CCEA702000F102C /* StudySessionResponseDTO.swift in Sources */, 4752A27D2CB96EB00073B784 /* CancleBag.swift in Sources */, + 47C815382CE21E640017EA24 /* ASAuthEndpoint.swift in Sources */, 47A9DCC32CE0DB97001DE76D /* WebView.swift in Sources */, 4736719F2CB120A600527896 /* WeeklyStudyView.swift in Sources */, 4780044C2CCAAD4F00FFAF00 /* WeekDay.swift in Sources */, diff --git a/HongikYeolgong2/Data/Repositories/Auth/SocialLoginRepositoryImpl.swift b/HongikYeolgong2/Data/Repositories/Auth/SocialLoginRepositoryImpl.swift new file mode 100644 index 0000000..553ee34 --- /dev/null +++ b/HongikYeolgong2/Data/Repositories/Auth/SocialLoginRepositoryImpl.swift @@ -0,0 +1,20 @@ +// +// SocialLoginRepository.swift +// HongikYeolgong2 +// +// Created by 권석기 on 11/11/24. +// + +import Combine + +final class SocialLoginRepositoryImpl { + func requestASToken() { + Task { + do { + + } catch { + + } + } + } +} diff --git a/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift b/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift index 4604bd8..4073503 100644 --- a/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift +++ b/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift @@ -185,28 +185,29 @@ final class UserDataMigrationInteractor: UserDataInteractor { } func withdraw() { - authService.performExistingAccountSetupFlows() + authService.performExistingAccountSetupFlows() .sink { _ in - } receiveValue: { appleIDcredential in - guard let appleIDcredential = appleIDcredential else { return } + } receiveValue: { _ in + } .store(in: cancleBag) - -// authRepository -// .withdraw() -// .sink(receiveCompletion: { _ in }) { [weak self] in -// guard let self = self else { return } -// appState.bulkUpdate { appState in -// appState.userSession = .unauthenticated -// appState.userData = .init() -// appState.permissions = .init() -// appState.studySession = .init() -// appState.system = .init() -// } -// KeyChainManager.deleteItem(key: .accessToken) -// } -// .store(in: cancleBag) + + + // authRepository + // .withdraw() + // .sink(receiveCompletion: { _ in }) { [weak self] in + // guard let self = self else { return } + // appState.bulkUpdate { appState in + // appState.userSession = .unauthenticated + // appState.userData = .init() + // appState.permissions = .init() + // appState.studySession = .init() + // appState.system = .init() + // } + // KeyChainManager.deleteItem(key: .accessToken) + // } + // .store(in: cancleBag) } } diff --git a/HongikYeolgong2/Domain/Interfaces/SocialLoginRepository.swift b/HongikYeolgong2/Domain/Interfaces/SocialLoginRepository.swift new file mode 100644 index 0000000..3dd9a6b --- /dev/null +++ b/HongikYeolgong2/Domain/Interfaces/SocialLoginRepository.swift @@ -0,0 +1,10 @@ +// +// SocialLoginRepository.swift +// HongikYeolgong2 +// +// Created by 권석기 on 11/11/24. +// + +import Foundation + +protocol SocialLoginRepository {} diff --git a/HongikYeolgong2/Info.plist b/HongikYeolgong2/Info.plist index 03527e0..7a0b6f0 100644 --- a/HongikYeolgong2/Info.plist +++ b/HongikYeolgong2/Info.plist @@ -2,6 +2,8 @@ + AppleAuthURL + $(APPLE_AUTH_URL) BaseURL $(BASE_URL) NSAppTransportSecurity diff --git a/HongikYeolgong2/Util/API/Endpoints/ASAuthEndpoint.swift b/HongikYeolgong2/Util/API/Endpoints/ASAuthEndpoint.swift new file mode 100644 index 0000000..9ec4f03 --- /dev/null +++ b/HongikYeolgong2/Util/API/Endpoints/ASAuthEndpoint.swift @@ -0,0 +1,54 @@ +// +// ASAuthEndpoint.swift +// HongikYeolgong2 +// +// Created by 권석기 on 11/11/24. +// + +import Foundation + +enum ASAuthEndpoint: EndpointProtocol { + case requestToken + + var baseURL: URL? { + switch self { + case .requestToken: + URL(string: "\(SecretKeys.appleAuthUrl)") + } + } + + var path: String { + switch self { + case .requestToken: + "/token" + } + } + + var method: NetworkMethod { + switch self { + case .requestToken: + .post + } + } + + var parameters: [URLQueryItem]? { + switch self { + case .requestToken: + nil + } + } + + var headers: [String : String]? { + switch self { + default: + ["Content-Type": "application/json", "form-data": "application/x-www-form-urlencoded"] + } + } + + var body: Data? { + switch self { + case .requestToken: + nil + } + } +} diff --git a/HongikYeolgong2/Util/Constants.swift b/HongikYeolgong2/Util/Constants.swift index 9c7eb0c..4127396 100644 --- a/HongikYeolgong2/Util/Constants.swift +++ b/HongikYeolgong2/Util/Constants.swift @@ -12,6 +12,7 @@ struct SecretKeys { static let roomStatusUrl = Bundle.main.infoDictionary?["RoomStatusURL"] as? String ?? "" static let noticeUrl = Bundle.main.infoDictionary?["NoticeURL"] as? String ?? "" static let qnaUrl = Bundle.main.infoDictionary?["QnaURL"] as? String ?? "" + static let appleAuthUrl = Bundle.main.infoDictionary?["AppleAuthURL"] as? String ?? "" } From b27cb4a60e3a8eead350a165e9337a510b5b666d Mon Sep 17 00:00:00 2001 From: Seokki-Kwon Date: Tue, 12 Nov 2024 01:05:55 +0900 Subject: [PATCH 4/9] =?UTF-8?q?feat:=20AppleID=20=ED=86=A0=ED=81=B0?= =?UTF-8?q?=EC=9E=AC=EB=B0=9C=EA=B8=89=20=EC=9A=94=EC=B2=AD=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HongikYeolgong2.xcodeproj/project.pbxproj | 29 ++++++++ .../xcshareddata/swiftpm/Package.resolved | 65 +++++++++++++++- HongikYeolgong2/Core/AppEnviroment.swift | 3 +- .../Data/DTO/Auth/ASTokenRequestDTO.swift | 17 +++++ .../Data/DTO/Auth/ASTokenResponseDTO.swift | 17 +++++ .../Auth/SocialLoginRepositoryImpl.swift | 22 ++++-- .../UserDataInteractor+Migration.swift | 74 ++++++++++++++++--- .../Interfaces/SocialLoginRepository.swift | 6 +- HongikYeolgong2/Info.plist | 8 +- .../Util/API/Endpoints/ASAuthEndpoint.swift | 17 +++-- HongikYeolgong2/Util/Constants.swift | 5 +- 11 files changed, 232 insertions(+), 31 deletions(-) create mode 100644 HongikYeolgong2/Data/DTO/Auth/ASTokenRequestDTO.swift create mode 100644 HongikYeolgong2/Data/DTO/Auth/ASTokenResponseDTO.swift diff --git a/HongikYeolgong2.xcodeproj/project.pbxproj b/HongikYeolgong2.xcodeproj/project.pbxproj index 824c18f..8a6703f 100644 --- a/HongikYeolgong2.xcodeproj/project.pbxproj +++ b/HongikYeolgong2.xcodeproj/project.pbxproj @@ -113,6 +113,10 @@ 47C815382CE21E640017EA24 /* ASAuthEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47C815372CE21E640017EA24 /* ASAuthEndpoint.swift */; }; 47C815412CE221060017EA24 /* SocialLoginRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47C815402CE221060017EA24 /* SocialLoginRepositoryImpl.swift */; }; 47C815432CE2211D0017EA24 /* SocialLoginRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47C815422CE2211D0017EA24 /* SocialLoginRepository.swift */; }; + 47C815462CE223DA0017EA24 /* ASTokenResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47C815452CE223DA0017EA24 /* ASTokenResponseDTO.swift */; }; + 47C815482CE227E50017EA24 /* ASTokenRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47C815472CE227E50017EA24 /* ASTokenRequestDTO.swift */; }; + 47C8154B2CE231810017EA24 /* SwiftJWT in Frameworks */ = {isa = PBXBuildFile; productRef = 47C8154A2CE231810017EA24 /* SwiftJWT */; }; + 47C815532CE257950017EA24 /* AuthKey_843UNB7W58.p8 in Resources */ = {isa = PBXBuildFile; fileRef = 47C815522CE257950017EA24 /* AuthKey_843UNB7W58.p8 */; }; 47CA17252CC9336100CBB251 /* StudyPeriodView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47CA17242CC9336100CBB251 /* StudyPeriodView.swift */; }; 47CA17272CC9340800CBB251 /* StudyTimerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47CA17262CC9340800CBB251 /* StudyTimerView.swift */; }; 47D5EDCD2CCBCB6D00ACA469 /* ImageBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D5EDCC2CCBCB6D00ACA469 /* ImageBackground.swift */; }; @@ -281,6 +285,9 @@ 47C815372CE21E640017EA24 /* ASAuthEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASAuthEndpoint.swift; sourceTree = ""; }; 47C815402CE221060017EA24 /* SocialLoginRepositoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialLoginRepositoryImpl.swift; sourceTree = ""; }; 47C815422CE2211D0017EA24 /* SocialLoginRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SocialLoginRepository.swift; sourceTree = ""; }; + 47C815452CE223DA0017EA24 /* ASTokenResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASTokenResponseDTO.swift; sourceTree = ""; }; + 47C815472CE227E50017EA24 /* ASTokenRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASTokenRequestDTO.swift; sourceTree = ""; }; + 47C815522CE257950017EA24 /* AuthKey_843UNB7W58.p8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AuthKey_843UNB7W58.p8; sourceTree = ""; }; 47CA17242CC9336100CBB251 /* StudyPeriodView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyPeriodView.swift; sourceTree = ""; }; 47CA17262CC9340800CBB251 /* StudyTimerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyTimerView.swift; sourceTree = ""; }; 47D5EDCC2CCBCB6D00ACA469 /* ImageBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageBackground.swift; sourceTree = ""; }; @@ -323,6 +330,7 @@ 47F71B592CDC68BC0044DEC5 /* FirebaseFirestoreCombine-Community in Frameworks */, 47F71B532CDC68BC0044DEC5 /* FirebaseAuthCombine-Community in Frameworks */, 47F71B632CDC813A0044DEC5 /* FirebaseFunctionsCombine-Community in Frameworks */, + 47C8154B2CE231810017EA24 /* SwiftJWT in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -779,6 +787,7 @@ 47B1D49F2C9CB1740071B62B = { isa = PBXGroup; children = ( + 47C815522CE257950017EA24 /* AuthKey_843UNB7W58.p8 */, 98B5F0092CDB362C007CF5FA /* Secrets-dev.xcconfig */, 479821D32CA24CFF002357EB /* .swiftlint.yml */, 47B1D4AA2C9CB1740071B62B /* HongikYeolgong2 */, @@ -903,6 +912,8 @@ 47F4F6982CC89A6900543D24 /* SignUpResponseDTO.swift */, 470483A92CDB532A00C381ED /* TokenValidResponseDTO.swift */, 4774C7BA2CDF632300CDD479 /* WithdrawResponseDTO.swift */, + 47C815452CE223DA0017EA24 /* ASTokenResponseDTO.swift */, + 47C815472CE227E50017EA24 /* ASTokenRequestDTO.swift */, ); path = Auth; sourceTree = ""; @@ -979,6 +990,7 @@ 47F71B5E2CDC813A0044DEC5 /* FirebaseDatabase */, 47F71B602CDC813A0044DEC5 /* FirebaseFunctions */, 47F71B622CDC813A0044DEC5 /* FirebaseFunctionsCombine-Community */, + 47C8154A2CE231810017EA24 /* SwiftJWT */, ); productName = HongikYeolgong2; productReference = 47B1D4A82C9CB1740071B62B /* HongikYeolgong2.app */; @@ -1054,6 +1066,7 @@ mainGroup = 47B1D49F2C9CB1740071B62B; packageReferences = ( 47F71B4F2CDC68BC0044DEC5 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + 47C815492CE231810017EA24 /* XCRemoteSwiftPackageReference "Swift-JWT" */, ); productRefGroup = 47B1D4A92C9CB1740071B62B /* Products */; projectDirPath = ""; @@ -1082,6 +1095,7 @@ 47A147652CA147A600A91F66 /* Pretendard-ExtraBold.otf in Resources */, 47B1D4B32C9CB1760071B62B /* Preview Assets.xcassets in Resources */, 47A1476D2CA147A600A91F66 /* SUITE-ExtraBold.otf in Resources */, + 47C815532CE257950017EA24 /* AuthKey_843UNB7W58.p8 in Resources */, 47A1476F2CA147A600A91F66 /* SUITE-Light.otf in Resources */, 47A147722CA147A600A91F66 /* SUITE-SemiBold.otf in Resources */, 47B1D4B02C9CB1760071B62B /* Assets.xcassets in Resources */, @@ -1166,6 +1180,7 @@ 47BACCF72CA164BA00295DAC /* Font+.swift in Sources */, 4786A1102CCA351A008635A4 /* WeeklyStudySessionDTO.swift in Sources */, 5E847A742CDBD52E0034C2A7 /* CalendarDataInteractor.swift in Sources */, + 47C815482CE227E50017EA24 /* ASTokenRequestDTO.swift in Sources */, 470722FA2CBC0A870046469F /* Constants.swift in Sources */, 478F84492CD359260097CAA1 /* WeeklyRanking.swift in Sources */, 4786A0EC2CC9E2BC008635A4 /* UserPermissionsInteractor.swift in Sources */, @@ -1210,6 +1225,7 @@ 47F71B5D2CDC77C60044DEC5 /* UserDataInteractor+Migration.swift in Sources */, 4763FFB52CB90EBD00990336 /* InitialView.swift in Sources */, 47E250712CCF274400267897 /* WeeklyStudyInteractor.swift in Sources */, + 47C815462CE223DA0017EA24 /* ASTokenResponseDTO.swift in Sources */, 470723092CBC198E0046469F /* AuthRepository.swift in Sources */, 47F4F6972CC88FBB00543D24 /* SignUpRequestDTO.swift in Sources */, 478004422CCA924200FFAF00 /* StudySessionMapper.swift in Sources */, @@ -1617,6 +1633,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 47C815492CE231810017EA24 /* XCRemoteSwiftPackageReference "Swift-JWT" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Kitura/Swift-JWT.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.0.1; + }; + }; 47F71B4F2CDC68BC0044DEC5 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git"; @@ -1628,6 +1652,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 47C8154A2CE231810017EA24 /* SwiftJWT */ = { + isa = XCSwiftPackageProductDependency; + package = 47C815492CE231810017EA24 /* XCRemoteSwiftPackageReference "Swift-JWT" */; + productName = SwiftJWT; + }; 47F71B502CDC68BC0044DEC5 /* FirebaseAuth */ = { isa = XCSwiftPackageProductDependency; package = 47F71B4F2CDC68BC0044DEC5 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; diff --git a/HongikYeolgong2.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/HongikYeolgong2.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6880c6f..8cb5085 100644 --- a/HongikYeolgong2.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/HongikYeolgong2.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "a1569f9895aa2be8e24832f98525d5da4eb90b5d158a82691c15b47eb72a13d7", + "originHash" : "37f87f153a7df0e69f7682478910cd31cbff8c536dc79a044414e38ba896c24f", "pins" : [ { "identity" : "abseil-cpp-binary", @@ -19,6 +19,33 @@ "version" : "11.2.0" } }, + { + "identity" : "bluecryptor", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Kitura/BlueCryptor.git", + "state" : { + "revision" : "cec97c24b111351e70e448972a7d3fe68a756d6d", + "version" : "2.0.2" + } + }, + { + "identity" : "blueecc", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Kitura/BlueECC.git", + "state" : { + "revision" : "1485268a54f8135435a825a855e733f026fa6cc8", + "version" : "1.2.201" + } + }, + { + "identity" : "bluersa", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Kitura/BlueRSA.git", + "state" : { + "revision" : "440f78db26d8bb073f29590f1c7bd31004da09ae", + "version" : "1.0.201" + } + }, { "identity" : "firebase-ios-sdk", "kind" : "remoteSourceControl", @@ -82,6 +109,15 @@ "version" : "100.0.0" } }, + { + "identity" : "kituracontracts", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Kitura/KituraContracts.git", + "state" : { + "revision" : "6edf7ac3dd2b3a2c61284778d430bbad7d8a6f23", + "version" : "2.0.1" + } + }, { "identity" : "leveldb", "kind" : "remoteSourceControl", @@ -91,6 +127,15 @@ "version" : "1.22.5" } }, + { + "identity" : "loggerapi", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Kitura/LoggerAPI.git", + "state" : { + "revision" : "4e6b45e850ffa275e8e26a24c6454fd709d5b6ac", + "version" : "2.0.0" + } + }, { "identity" : "nanopb", "kind" : "remoteSourceControl", @@ -109,6 +154,24 @@ "version" : "2.4.0" } }, + { + "identity" : "swift-jwt", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Kitura/Swift-JWT.git", + "state" : { + "revision" : "f68ec28fbd90a651597e9e825ea7f315f8d52a1f", + "version" : "4.0.1" + } + }, + { + "identity" : "swift-log", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-log.git", + "state" : { + "revision" : "9cb486020ebf03bfa5b5df985387a14a98744537", + "version" : "1.6.1" + } + }, { "identity" : "swift-protobuf", "kind" : "remoteSourceControl", diff --git a/HongikYeolgong2/Core/AppEnviroment.swift b/HongikYeolgong2/Core/AppEnviroment.swift index 86da4b2..7274327 100644 --- a/HongikYeolgong2/Core/AppEnviroment.swift +++ b/HongikYeolgong2/Core/AppEnviroment.swift @@ -64,7 +64,8 @@ extension AppEnviroment { userDataInteractor: UserDataMigrationInteractor( appState: appState, authRepository: remoteRepository.authRepository, - authService: services.appleAuthService + socialLoginRepository: SocialLoginRepositoryImpl(), + appleLoginService: services.appleAuthService ), studyTimeInteractor: StudyTimeInteractorImpl( studySessionRepository: remoteRepository.studySessionRepository diff --git a/HongikYeolgong2/Data/DTO/Auth/ASTokenRequestDTO.swift b/HongikYeolgong2/Data/DTO/Auth/ASTokenRequestDTO.swift new file mode 100644 index 0000000..e2fa88e --- /dev/null +++ b/HongikYeolgong2/Data/DTO/Auth/ASTokenRequestDTO.swift @@ -0,0 +1,17 @@ +// +// ASTokenRequestDTO.swift +// HongikYeolgong2 +// +// Created by 권석기 on 11/11/24. +// + +import Foundation + +struct ASTokenRequestDTO: Encodable { + let client_id: String + let client_secret: String + let grant_type: String + let code: String + var refresh_token: String? + var redirect_uri: String? +} diff --git a/HongikYeolgong2/Data/DTO/Auth/ASTokenResponseDTO.swift b/HongikYeolgong2/Data/DTO/Auth/ASTokenResponseDTO.swift new file mode 100644 index 0000000..97f148a --- /dev/null +++ b/HongikYeolgong2/Data/DTO/Auth/ASTokenResponseDTO.swift @@ -0,0 +1,17 @@ +// +// ASTokenResponseDTO.swift +// HongikYeolgong2 +// +// Created by 권석기 on 11/11/24. +// + +import Foundation + +struct ASTokenResponseDTO: Decodable { + let access_token: String + let expires_in: Int + let id_token: String + let refresh_token: String + let token_type: String +} + diff --git a/HongikYeolgong2/Data/Repositories/Auth/SocialLoginRepositoryImpl.swift b/HongikYeolgong2/Data/Repositories/Auth/SocialLoginRepositoryImpl.swift index 553ee34..9680198 100644 --- a/HongikYeolgong2/Data/Repositories/Auth/SocialLoginRepositoryImpl.swift +++ b/HongikYeolgong2/Data/Repositories/Auth/SocialLoginRepositoryImpl.swift @@ -7,14 +7,20 @@ import Combine -final class SocialLoginRepositoryImpl { - func requestASToken() { - Task { - do { - - } catch { - +final class SocialLoginRepositoryImpl: SocialLoginRepository { + func requestASToken(asTokenRequestDto: ASTokenRequestDTO) -> AnyPublisher { + return Future { promise in + Task { + do { + let response: BaseResponse = try await NetworkService.shared.request(endpoint: ASAuthEndpoint.requestToken(asTokenRequestDto)) + print(response.data) + promise(.success(response.data)) + } + catch let error as NetworkError { + print(error.message) + promise(.failure(error)) + } } - } + }.eraseToAnyPublisher() } } diff --git a/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift b/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift index 4073503..2586cb0 100644 --- a/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift +++ b/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift @@ -13,27 +13,31 @@ import SwiftUI import FirebaseAuth import FirebaseCore import FirebaseFirestore +import SwiftJWT final class UserDataMigrationInteractor: UserDataInteractor { private let cancleBag = CancelBag() private let appState: Store private let authRepository: AuthRepository - private let authService: AppleLoginService + private let socialLoginRepository: SocialLoginRepository + private let appleLoginService: AppleLoginService private let db = Firestore.firestore() init(appState: Store, authRepository: AuthRepository, - authService: AppleLoginService) { + socialLoginRepository: SocialLoginRepository, + appleLoginService: AppleLoginService) { self.appState = appState self.authRepository = authRepository - self.authService = authService + self.appleLoginService = appleLoginService + self.socialLoginRepository = socialLoginRepository } /// 애플로그인을 요청합니다. /// - Parameter authorization: ASAuthorization func requestAppleLogin(_ authorization: ASAuthorization) { - guard let appleIDCredential = authService.requestAppleLogin(authorization), + guard let appleIDCredential = appleLoginService.requestAppleLogin(authorization), let idTokenData = appleIDCredential.identityToken, let idToken = String(data: idTokenData, encoding: .utf8) else { return @@ -184,13 +188,33 @@ final class UserDataMigrationInteractor: UserDataInteractor { .store(in: cancleBag) } - func withdraw() { - authService.performExistingAccountSetupFlows() - .sink { _ in + func withdraw() { + appleLoginService.performExistingAccountSetupFlows() + .mapError({ error in + NetworkError.decodingError("") + }) + .flatMap({ [weak self] appleIDCrendential -> AnyPublisher in + guard let self = self, + let appleIDCredential = appleIDCrendential, + let authorizationCodeData = appleIDCredential.authorizationCode, + let authorizationCode = String(data: authorizationCodeData, encoding: .utf8) else { + return Fail(error: NetworkError.decodingError("could not decoded appleIDCredential")).eraseToAnyPublisher() + } - } receiveValue: { _ in + let clientSecret = makeJWT() - } + let asTokenRequestDTO: ASTokenRequestDTO = .init(client_id: SecretKeys.bundleName, + client_secret: clientSecret, + grant_type: "authorization_code", + code: authorizationCode) + + return socialLoginRepository.requestASToken(asTokenRequestDto: asTokenRequestDTO) + }) + .sink(receiveCompletion: { _ in + + }, receiveValue: { _ in + + }) .store(in: cancleBag) @@ -244,4 +268,36 @@ extension UserDataMigrationInteractor { return hashString } + func makeJWT() -> String { + let myHeader = Header(kid: SecretKeys.serviceID) + struct MyClaims: Claims { + let iss: String + let iat: Int + let exp: Int + let aud: String + let sub: String + } + + let iat = Int(Date().timeIntervalSince1970) + let exp = iat + 3600 + let myClaims = MyClaims(iss: SecretKeys.teamID, + iat: iat, + exp: exp, + aud: "https://appleid.apple.com", + sub: SecretKeys.bundleName) + + var myJWT = JWT(header: myHeader, claims: myClaims) + + guard let url = Bundle.main.url(forResource: "AuthKey_\(SecretKeys.serviceID)", withExtension: "p8") else { + return "" + } + + let privateKey: Data = try! Data(contentsOf: url, options: .alwaysMapped) + + let jwtSigner = JWTSigner.es256(privateKey: privateKey) + let signedJWT = try! myJWT.sign(using: jwtSigner) + + return signedJWT + } + } diff --git a/HongikYeolgong2/Domain/Interfaces/SocialLoginRepository.swift b/HongikYeolgong2/Domain/Interfaces/SocialLoginRepository.swift index 3dd9a6b..bca2209 100644 --- a/HongikYeolgong2/Domain/Interfaces/SocialLoginRepository.swift +++ b/HongikYeolgong2/Domain/Interfaces/SocialLoginRepository.swift @@ -5,6 +5,8 @@ // Created by 권석기 on 11/11/24. // -import Foundation +import Combine -protocol SocialLoginRepository {} +protocol SocialLoginRepository { + func requestASToken(asTokenRequestDto: ASTokenRequestDTO) -> AnyPublisher +} diff --git a/HongikYeolgong2/Info.plist b/HongikYeolgong2/Info.plist index 7a0b6f0..0babe6d 100644 --- a/HongikYeolgong2/Info.plist +++ b/HongikYeolgong2/Info.plist @@ -2,8 +2,8 @@ - AppleAuthURL - $(APPLE_AUTH_URL) + AppleIdURL + $(APPLEID_API_URL) BaseURL $(BASE_URL) NSAppTransportSecurity @@ -17,6 +17,10 @@ $(QNA_URL) RoomStatusURL $(ROOM_STATUS_URL) + ServiceID + $(SERVICE_ID) + TeamID + $(TEAM_ID) UIAppFonts SUITE-SemiBold.otf diff --git a/HongikYeolgong2/Util/API/Endpoints/ASAuthEndpoint.swift b/HongikYeolgong2/Util/API/Endpoints/ASAuthEndpoint.swift index 9ec4f03..a11514e 100644 --- a/HongikYeolgong2/Util/API/Endpoints/ASAuthEndpoint.swift +++ b/HongikYeolgong2/Util/API/Endpoints/ASAuthEndpoint.swift @@ -8,12 +8,12 @@ import Foundation enum ASAuthEndpoint: EndpointProtocol { - case requestToken + case requestToken(ASTokenRequestDTO) var baseURL: URL? { switch self { case .requestToken: - URL(string: "\(SecretKeys.appleAuthUrl)") + URL(string: "\(SecretKeys.appleIDApiUrl)") } } @@ -27,28 +27,31 @@ enum ASAuthEndpoint: EndpointProtocol { var method: NetworkMethod { switch self { case .requestToken: - .post + .post } } var parameters: [URLQueryItem]? { switch self { - case .requestToken: - nil + case let .requestToken(asTokenRequestDto): + [URLQueryItem(name: "client_id", value: asTokenRequestDto.client_id), + URLQueryItem(name: "client_secret", value: asTokenRequestDto.client_secret), + URLQueryItem(name: "code", value: asTokenRequestDto.code), + URLQueryItem(name: "grant_type", value: asTokenRequestDto.grant_type)] } } var headers: [String : String]? { switch self { default: - ["Content-Type": "application/json", "form-data": "application/x-www-form-urlencoded"] + ["Content-Type": "application/x-www-form-urlencoded"] } } var body: Data? { switch self { case .requestToken: - nil + return nil } } } diff --git a/HongikYeolgong2/Util/Constants.swift b/HongikYeolgong2/Util/Constants.swift index 4127396..de34446 100644 --- a/HongikYeolgong2/Util/Constants.swift +++ b/HongikYeolgong2/Util/Constants.swift @@ -12,7 +12,10 @@ struct SecretKeys { static let roomStatusUrl = Bundle.main.infoDictionary?["RoomStatusURL"] as? String ?? "" static let noticeUrl = Bundle.main.infoDictionary?["NoticeURL"] as? String ?? "" static let qnaUrl = Bundle.main.infoDictionary?["QnaURL"] as? String ?? "" - static let appleAuthUrl = Bundle.main.infoDictionary?["AppleAuthURL"] as? String ?? "" + static let appleIDApiUrl = Bundle.main.infoDictionary?["AppleIdURL"] as? String ?? "" + static let bundleName = Bundle.main.bundleIdentifier ?? "" + static let teamID = Bundle.main.infoDictionary?["TeamID"] as? String ?? "" + static let serviceID = Bundle.main.infoDictionary?["ServiceID"] as? String ?? "" } From 07e484b6933a1506bebc00412a4c3caec535bf9d Mon Sep 17 00:00:00 2001 From: Seokki-Kwon Date: Tue, 12 Nov 2024 01:54:04 +0900 Subject: [PATCH 5/9] =?UTF-8?q?fix:=20=ED=86=A0=ED=81=B0=EC=9D=91=EB=8B=B5?= =?UTF-8?q?=20=EB=94=94=EC=BD=94=EB=94=A9=EC=97=90=EB=9F=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HongikYeolgong2/Data/DTO/Auth/ASTokenResponseDTO.swift | 10 +++++----- .../Repositories/Auth/SocialLoginRepositoryImpl.swift | 8 +++----- HongikYeolgong2/Util/API/Base/NetworkService.swift | 7 +++++-- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/HongikYeolgong2/Data/DTO/Auth/ASTokenResponseDTO.swift b/HongikYeolgong2/Data/DTO/Auth/ASTokenResponseDTO.swift index 97f148a..91a8970 100644 --- a/HongikYeolgong2/Data/DTO/Auth/ASTokenResponseDTO.swift +++ b/HongikYeolgong2/Data/DTO/Auth/ASTokenResponseDTO.swift @@ -8,10 +8,10 @@ import Foundation struct ASTokenResponseDTO: Decodable { - let access_token: String - let expires_in: Int - let id_token: String - let refresh_token: String - let token_type: String + let accessToken: String + let expiresIn: Int + let idToken: String + let refreshToken: String + let tokenType: String } diff --git a/HongikYeolgong2/Data/Repositories/Auth/SocialLoginRepositoryImpl.swift b/HongikYeolgong2/Data/Repositories/Auth/SocialLoginRepositoryImpl.swift index 9680198..b1e71bb 100644 --- a/HongikYeolgong2/Data/Repositories/Auth/SocialLoginRepositoryImpl.swift +++ b/HongikYeolgong2/Data/Repositories/Auth/SocialLoginRepositoryImpl.swift @@ -12,12 +12,10 @@ final class SocialLoginRepositoryImpl: SocialLoginRepository { return Future { promise in Task { do { - let response: BaseResponse = try await NetworkService.shared.request(endpoint: ASAuthEndpoint.requestToken(asTokenRequestDto)) - print(response.data) - promise(.success(response.data)) + let response: ASTokenResponseDTO = try await NetworkService.shared.request(endpoint: ASAuthEndpoint.requestToken(asTokenRequestDto)) + promise(.success(response)) } - catch let error as NetworkError { - print(error.message) + catch let error as NetworkError { promise(.failure(error)) } } diff --git a/HongikYeolgong2/Util/API/Base/NetworkService.swift b/HongikYeolgong2/Util/API/Base/NetworkService.swift index 8f3ae1c..3ab7f72 100644 --- a/HongikYeolgong2/Util/API/Base/NetworkService.swift +++ b/HongikYeolgong2/Util/API/Base/NetworkService.swift @@ -23,7 +23,7 @@ struct NetworkService: NetworkProtocol { let (data, response) = try await session.data(for: request) return try processResponse(data: data, response: response) - } + } } extension NetworkService { @@ -66,13 +66,16 @@ extension NetworkService { guard (200...299).contains(httpResponse.statusCode) else { throw NetworkError.serverError(statusCode: httpResponse.statusCode) } - + if let jsonString = String(data: data, encoding: .utf8) { + print(jsonString) + } do { let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase return try decoder.decode(T.self, from: data) } catch { + print(error) throw NetworkError.decodingError(error.localizedDescription) } } From 9589ff92433288a2669a8f4f4534dd6614082544 Mon Sep 17 00:00:00 2001 From: Seokki-Kwon Date: Tue, 12 Nov 2024 02:01:39 +0900 Subject: [PATCH 6/9] =?UTF-8?q?feat:=20RevokeTokenDTO,=20Endpoint=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HongikYeolgong2.xcodeproj/project.pbxproj | 4 ++++ .../Data/DTO/Auth/ASRevokeTokenRequestDTO.swift | 15 +++++++++++++++ .../UserDataInteractor+Migration.swift | 4 ++-- .../Util/API/Base/NetworkService.swift | 4 +--- .../Util/API/Endpoints/ASAuthEndpoint.swift | 13 +++++++++---- 5 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 HongikYeolgong2/Data/DTO/Auth/ASRevokeTokenRequestDTO.swift diff --git a/HongikYeolgong2.xcodeproj/project.pbxproj b/HongikYeolgong2.xcodeproj/project.pbxproj index 8a6703f..c0378c0 100644 --- a/HongikYeolgong2.xcodeproj/project.pbxproj +++ b/HongikYeolgong2.xcodeproj/project.pbxproj @@ -117,6 +117,7 @@ 47C815482CE227E50017EA24 /* ASTokenRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47C815472CE227E50017EA24 /* ASTokenRequestDTO.swift */; }; 47C8154B2CE231810017EA24 /* SwiftJWT in Frameworks */ = {isa = PBXBuildFile; productRef = 47C8154A2CE231810017EA24 /* SwiftJWT */; }; 47C815532CE257950017EA24 /* AuthKey_843UNB7W58.p8 in Resources */ = {isa = PBXBuildFile; fileRef = 47C815522CE257950017EA24 /* AuthKey_843UNB7W58.p8 */; }; + 47C815552CE26EE40017EA24 /* ASRevokeTokenRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47C815542CE26EE40017EA24 /* ASRevokeTokenRequestDTO.swift */; }; 47CA17252CC9336100CBB251 /* StudyPeriodView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47CA17242CC9336100CBB251 /* StudyPeriodView.swift */; }; 47CA17272CC9340800CBB251 /* StudyTimerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47CA17262CC9340800CBB251 /* StudyTimerView.swift */; }; 47D5EDCD2CCBCB6D00ACA469 /* ImageBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D5EDCC2CCBCB6D00ACA469 /* ImageBackground.swift */; }; @@ -288,6 +289,7 @@ 47C815452CE223DA0017EA24 /* ASTokenResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASTokenResponseDTO.swift; sourceTree = ""; }; 47C815472CE227E50017EA24 /* ASTokenRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASTokenRequestDTO.swift; sourceTree = ""; }; 47C815522CE257950017EA24 /* AuthKey_843UNB7W58.p8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AuthKey_843UNB7W58.p8; sourceTree = ""; }; + 47C815542CE26EE40017EA24 /* ASRevokeTokenRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASRevokeTokenRequestDTO.swift; sourceTree = ""; }; 47CA17242CC9336100CBB251 /* StudyPeriodView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyPeriodView.swift; sourceTree = ""; }; 47CA17262CC9340800CBB251 /* StudyTimerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyTimerView.swift; sourceTree = ""; }; 47D5EDCC2CCBCB6D00ACA469 /* ImageBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageBackground.swift; sourceTree = ""; }; @@ -914,6 +916,7 @@ 4774C7BA2CDF632300CDD479 /* WithdrawResponseDTO.swift */, 47C815452CE223DA0017EA24 /* ASTokenResponseDTO.swift */, 47C815472CE227E50017EA24 /* ASTokenRequestDTO.swift */, + 47C815542CE26EE40017EA24 /* ASRevokeTokenRequestDTO.swift */, ); path = Auth; sourceTree = ""; @@ -1255,6 +1258,7 @@ 473E8EB22CCE5267000F102C /* SystemOverlay.swift in Sources */, 475B86DF2CA1AF1E000534B2 /* RecordView.swift in Sources */, 98F9D8462CDA2E6D00DE12BB /* RecordCell.swift in Sources */, + 47C815552CE26EE40017EA24 /* ASRevokeTokenRequestDTO.swift in Sources */, 47A147792CA158E800A91F66 /* MainTabView.swift in Sources */, 47F79B302CCCB7FC00DD0899 /* TimePicker.swift in Sources */, 478FBCA32CD13A1E00256012 /* UserInfo.swift in Sources */, diff --git a/HongikYeolgong2/Data/DTO/Auth/ASRevokeTokenRequestDTO.swift b/HongikYeolgong2/Data/DTO/Auth/ASRevokeTokenRequestDTO.swift new file mode 100644 index 0000000..dbd7ba1 --- /dev/null +++ b/HongikYeolgong2/Data/DTO/Auth/ASRevokeTokenRequestDTO.swift @@ -0,0 +1,15 @@ +// +// ASRevokeTokenRequestDTO.swift +// HongikYeolgong2 +// +// Created by 권석기 on 11/12/24. +// + +import Foundation + +struct ASRevokeTokenRequestDTO { + let client_id: String + let client_secret: String + let token: String + let token_type_hint: String +} diff --git a/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift b/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift index 2586cb0..98161ca 100644 --- a/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift +++ b/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift @@ -188,7 +188,7 @@ final class UserDataMigrationInteractor: UserDataInteractor { .store(in: cancleBag) } - func withdraw() { + func withdraw() { appleLoginService.performExistingAccountSetupFlows() .mapError({ error in NetworkError.decodingError("") @@ -209,7 +209,7 @@ final class UserDataMigrationInteractor: UserDataInteractor { code: authorizationCode) return socialLoginRepository.requestASToken(asTokenRequestDto: asTokenRequestDTO) - }) + }) .sink(receiveCompletion: { _ in }, receiveValue: { _ in diff --git a/HongikYeolgong2/Util/API/Base/NetworkService.swift b/HongikYeolgong2/Util/API/Base/NetworkService.swift index 3ab7f72..6f8e879 100644 --- a/HongikYeolgong2/Util/API/Base/NetworkService.swift +++ b/HongikYeolgong2/Util/API/Base/NetworkService.swift @@ -66,9 +66,7 @@ extension NetworkService { guard (200...299).contains(httpResponse.statusCode) else { throw NetworkError.serverError(statusCode: httpResponse.statusCode) } - if let jsonString = String(data: data, encoding: .utf8) { - print(jsonString) - } + do { let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase diff --git a/HongikYeolgong2/Util/API/Endpoints/ASAuthEndpoint.swift b/HongikYeolgong2/Util/API/Endpoints/ASAuthEndpoint.swift index a11514e..d72df75 100644 --- a/HongikYeolgong2/Util/API/Endpoints/ASAuthEndpoint.swift +++ b/HongikYeolgong2/Util/API/Endpoints/ASAuthEndpoint.swift @@ -9,10 +9,11 @@ import Foundation enum ASAuthEndpoint: EndpointProtocol { case requestToken(ASTokenRequestDTO) + case requestRevoke(ASRevokeTokenRequestDTO) var baseURL: URL? { switch self { - case .requestToken: + default: URL(string: "\(SecretKeys.appleIDApiUrl)") } } @@ -21,12 +22,14 @@ enum ASAuthEndpoint: EndpointProtocol { switch self { case .requestToken: "/token" + case .requestRevoke: + "/revoke" } } var method: NetworkMethod { switch self { - case .requestToken: + case .requestToken, .requestRevoke: .post } } @@ -34,10 +37,12 @@ enum ASAuthEndpoint: EndpointProtocol { var parameters: [URLQueryItem]? { switch self { case let .requestToken(asTokenRequestDto): - [URLQueryItem(name: "client_id", value: asTokenRequestDto.client_id), + return [URLQueryItem(name: "client_id", value: asTokenRequestDto.client_id), URLQueryItem(name: "client_secret", value: asTokenRequestDto.client_secret), URLQueryItem(name: "code", value: asTokenRequestDto.code), URLQueryItem(name: "grant_type", value: asTokenRequestDto.grant_type)] + case let .requestRevoke(asRevokeTokenRequestDto): + return nil } } @@ -50,7 +55,7 @@ enum ASAuthEndpoint: EndpointProtocol { var body: Data? { switch self { - case .requestToken: + case .requestToken, .requestRevoke: return nil } } From 21fdd2d274b8310e8fb8537cc365daceafe8b0ab Mon Sep 17 00:00:00 2001 From: Seokki-Kwon Date: Tue, 12 Nov 2024 02:37:11 +0900 Subject: [PATCH 7/9] =?UTF-8?q?fix:=20NetworkService=20=EB=A6=AC=ED=84=B4?= =?UTF-8?q?=ED=83=80=EC=9E=85=EC=9D=B4=20=EC=97=86=EB=8A=94=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Auth/SocialLoginRepositoryImpl.swift | 16 ++++- .../UserDataInteractor+Migration.swift | 59 +++++++++++-------- .../Interfaces/SocialLoginRepository.swift | 1 + .../Util/API/Base/NetworkService.swift | 19 +++++- .../Util/API/Endpoints/ASAuthEndpoint.swift | 10 ++-- 5 files changed, 75 insertions(+), 30 deletions(-) diff --git a/HongikYeolgong2/Data/Repositories/Auth/SocialLoginRepositoryImpl.swift b/HongikYeolgong2/Data/Repositories/Auth/SocialLoginRepositoryImpl.swift index b1e71bb..e535239 100644 --- a/HongikYeolgong2/Data/Repositories/Auth/SocialLoginRepositoryImpl.swift +++ b/HongikYeolgong2/Data/Repositories/Auth/SocialLoginRepositoryImpl.swift @@ -8,6 +8,20 @@ import Combine final class SocialLoginRepositoryImpl: SocialLoginRepository { + func requestASTokenRevoke(asRevokeTokenRequestDto: ASRevokeTokenRequestDTO) -> AnyPublisher { + return Future { promise in + Task { + do { + let _ = try await NetworkService.shared.plainRequest(endpoint: ASAuthEndpoint.requestRevoke(asRevokeTokenRequestDto)) + promise(.success(())) + } + catch let error as NetworkError { + promise(.failure(error)) + } + } + }.eraseToAnyPublisher() + } + func requestASToken(asTokenRequestDto: ASTokenRequestDTO) -> AnyPublisher { return Future { promise in Task { @@ -15,7 +29,7 @@ final class SocialLoginRepositoryImpl: SocialLoginRepository { let response: ASTokenResponseDTO = try await NetworkService.shared.request(endpoint: ASAuthEndpoint.requestToken(asTokenRequestDto)) promise(.success(response)) } - catch let error as NetworkError { + catch let error as NetworkError { promise(.failure(error)) } } diff --git a/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift b/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift index 98161ca..7b23f90 100644 --- a/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift +++ b/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift @@ -189,6 +189,8 @@ final class UserDataMigrationInteractor: UserDataInteractor { } func withdraw() { + let clientSecret = makeJWT() + appleLoginService.performExistingAccountSetupFlows() .mapError({ error in NetworkError.decodingError("") @@ -201,37 +203,46 @@ final class UserDataMigrationInteractor: UserDataInteractor { return Fail(error: NetworkError.decodingError("could not decoded appleIDCredential")).eraseToAnyPublisher() } - let clientSecret = makeJWT() - - let asTokenRequestDTO: ASTokenRequestDTO = .init(client_id: SecretKeys.bundleName, + let asTokenRequestDto: ASTokenRequestDTO = .init(client_id: SecretKeys.bundleName, client_secret: clientSecret, grant_type: "authorization_code", code: authorizationCode) - return socialLoginRepository.requestASToken(asTokenRequestDto: asTokenRequestDTO) - }) - .sink(receiveCompletion: { _ in + return socialLoginRepository.requestASToken(asTokenRequestDto: asTokenRequestDto) + }) + .flatMap({ [weak self] asTokenResponseDto -> AnyPublisher in + guard let self = self else { + return Fail(outputType: Void.self, failure: NetworkError.decodingError("")).eraseToAnyPublisher() + } - }, receiveValue: { _ in + let asRevokeTokenRequestDto: ASRevokeTokenRequestDTO = .init( + client_id: SecretKeys.bundleName, + client_secret: clientSecret, + token: asTokenResponseDto.accessToken, + token_type_hint: "") + return self.socialLoginRepository.requestASTokenRevoke(asRevokeTokenRequestDto: asRevokeTokenRequestDto) + }) + .flatMap({ [weak self] _ in + guard let self = self else { + return Fail(outputType: Void.self, failure: NetworkError.decodingError("")).eraseToAnyPublisher() + } + return authRepository.withdraw() + }) + .sink(receiveCompletion: { _ in + + }, receiveValue: { [weak self] in + guard let self = self else { return } + appState.bulkUpdate { appState in + appState.userSession = .unauthenticated + appState.userData = .init() + appState.permissions = .init() + appState.studySession = .init() + appState.system = .init() + } + KeyChainManager.deleteItem(key: .accessToken) }) .store(in: cancleBag) - - - // authRepository - // .withdraw() - // .sink(receiveCompletion: { _ in }) { [weak self] in - // guard let self = self else { return } - // appState.bulkUpdate { appState in - // appState.userSession = .unauthenticated - // appState.userData = .init() - // appState.permissions = .init() - // appState.studySession = .init() - // appState.system = .init() - // } - // KeyChainManager.deleteItem(key: .accessToken) - // } - // .store(in: cancleBag) } } @@ -277,7 +288,7 @@ extension UserDataMigrationInteractor { let aud: String let sub: String } - + let iat = Int(Date().timeIntervalSince1970) let exp = iat + 3600 let myClaims = MyClaims(iss: SecretKeys.teamID, diff --git a/HongikYeolgong2/Domain/Interfaces/SocialLoginRepository.swift b/HongikYeolgong2/Domain/Interfaces/SocialLoginRepository.swift index bca2209..f1e8cd8 100644 --- a/HongikYeolgong2/Domain/Interfaces/SocialLoginRepository.swift +++ b/HongikYeolgong2/Domain/Interfaces/SocialLoginRepository.swift @@ -9,4 +9,5 @@ import Combine protocol SocialLoginRepository { func requestASToken(asTokenRequestDto: ASTokenRequestDTO) -> AnyPublisher + func requestASTokenRevoke(asRevokeTokenRequestDto: ASRevokeTokenRequestDTO) -> AnyPublisher } diff --git a/HongikYeolgong2/Util/API/Base/NetworkService.swift b/HongikYeolgong2/Util/API/Base/NetworkService.swift index 6f8e879..8532589 100644 --- a/HongikYeolgong2/Util/API/Base/NetworkService.swift +++ b/HongikYeolgong2/Util/API/Base/NetworkService.swift @@ -24,6 +24,23 @@ struct NetworkService: NetworkProtocol { return try processResponse(data: data, response: response) } + + func plainRequest(endpoint: EndpointProtocol) async throws { + guard let url = configUrl(endpoint: endpoint) else { + throw NetworkError.invalidUrl + } + + let request = configRequest(url: url, endpoint: endpoint) + let (data, response) = try await session.data(for: request) + + guard let httpResponse = response as? HTTPURLResponse else { + throw NetworkError.invalidResponse + } + + guard (200...299).contains(httpResponse.statusCode) else { + throw NetworkError.serverError(statusCode: httpResponse.statusCode) + } + } } extension NetworkService { @@ -66,7 +83,7 @@ extension NetworkService { guard (200...299).contains(httpResponse.statusCode) else { throw NetworkError.serverError(statusCode: httpResponse.statusCode) } - + do { let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase diff --git a/HongikYeolgong2/Util/API/Endpoints/ASAuthEndpoint.swift b/HongikYeolgong2/Util/API/Endpoints/ASAuthEndpoint.swift index d72df75..ef132b9 100644 --- a/HongikYeolgong2/Util/API/Endpoints/ASAuthEndpoint.swift +++ b/HongikYeolgong2/Util/API/Endpoints/ASAuthEndpoint.swift @@ -38,11 +38,13 @@ enum ASAuthEndpoint: EndpointProtocol { switch self { case let .requestToken(asTokenRequestDto): return [URLQueryItem(name: "client_id", value: asTokenRequestDto.client_id), - URLQueryItem(name: "client_secret", value: asTokenRequestDto.client_secret), - URLQueryItem(name: "code", value: asTokenRequestDto.code), - URLQueryItem(name: "grant_type", value: asTokenRequestDto.grant_type)] + URLQueryItem(name: "client_secret", value: asTokenRequestDto.client_secret), + URLQueryItem(name: "code", value: asTokenRequestDto.code), + URLQueryItem(name: "grant_type", value: asTokenRequestDto.grant_type)] case let .requestRevoke(asRevokeTokenRequestDto): - return nil + return [URLQueryItem(name: "client_id", value: asRevokeTokenRequestDto.client_id), + URLQueryItem(name: "client_secret", value: asRevokeTokenRequestDto.client_secret), + URLQueryItem(name: "token", value: asRevokeTokenRequestDto.token)] } } From 85bd93cb1a9fe9d9daefe540a08b8423b229a44e Mon Sep 17 00:00:00 2001 From: Seokki-Kwon Date: Tue, 12 Nov 2024 03:13:37 +0900 Subject: [PATCH 8/9] =?UTF-8?q?refactor:=20OnboardingView=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=B3=84=EB=A1=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HongikYeolgong2.xcodeproj/project.pbxproj | 16 ++++ .../UserDataInteractor+Migration.swift | 1 + .../Component/AppleLoginButton.swift | 36 ++++++++ .../Component/OnboardingPageView.swift | 45 +++++++++ .../Auth/Onboarding/OnboardingView.swift | 92 ++++--------------- .../Util/Services/AppleLoginManager.swift | 29 +++--- 6 files changed, 134 insertions(+), 85 deletions(-) create mode 100644 HongikYeolgong2/Presentation/Auth/Onboarding/Component/AppleLoginButton.swift create mode 100644 HongikYeolgong2/Presentation/Auth/Onboarding/Component/OnboardingPageView.swift diff --git a/HongikYeolgong2.xcodeproj/project.pbxproj b/HongikYeolgong2.xcodeproj/project.pbxproj index c0378c0..b4051cc 100644 --- a/HongikYeolgong2.xcodeproj/project.pbxproj +++ b/HongikYeolgong2.xcodeproj/project.pbxproj @@ -118,6 +118,8 @@ 47C8154B2CE231810017EA24 /* SwiftJWT in Frameworks */ = {isa = PBXBuildFile; productRef = 47C8154A2CE231810017EA24 /* SwiftJWT */; }; 47C815532CE257950017EA24 /* AuthKey_843UNB7W58.p8 in Resources */ = {isa = PBXBuildFile; fileRef = 47C815522CE257950017EA24 /* AuthKey_843UNB7W58.p8 */; }; 47C815552CE26EE40017EA24 /* ASRevokeTokenRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47C815542CE26EE40017EA24 /* ASRevokeTokenRequestDTO.swift */; }; + 47C815582CE27E140017EA24 /* OnboardingPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47C815572CE27E140017EA24 /* OnboardingPageView.swift */; }; + 47C8155A2CE27EB00017EA24 /* AppleLoginButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47C815592CE27EB00017EA24 /* AppleLoginButton.swift */; }; 47CA17252CC9336100CBB251 /* StudyPeriodView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47CA17242CC9336100CBB251 /* StudyPeriodView.swift */; }; 47CA17272CC9340800CBB251 /* StudyTimerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47CA17262CC9340800CBB251 /* StudyTimerView.swift */; }; 47D5EDCD2CCBCB6D00ACA469 /* ImageBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47D5EDCC2CCBCB6D00ACA469 /* ImageBackground.swift */; }; @@ -290,6 +292,8 @@ 47C815472CE227E50017EA24 /* ASTokenRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASTokenRequestDTO.swift; sourceTree = ""; }; 47C815522CE257950017EA24 /* AuthKey_843UNB7W58.p8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AuthKey_843UNB7W58.p8; sourceTree = ""; }; 47C815542CE26EE40017EA24 /* ASRevokeTokenRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASRevokeTokenRequestDTO.swift; sourceTree = ""; }; + 47C815572CE27E140017EA24 /* OnboardingPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingPageView.swift; sourceTree = ""; }; + 47C815592CE27EB00017EA24 /* AppleLoginButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleLoginButton.swift; sourceTree = ""; }; 47CA17242CC9336100CBB251 /* StudyPeriodView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyPeriodView.swift; sourceTree = ""; }; 47CA17262CC9340800CBB251 /* StudyTimerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyTimerView.swift; sourceTree = ""; }; 47D5EDCC2CCBCB6D00ACA469 /* ImageBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageBackground.swift; sourceTree = ""; }; @@ -773,6 +777,7 @@ 47A5406E2CD0B8AB00DC40D0 /* Onboarding */ = { isa = PBXGroup; children = ( + 47C815562CE27E050017EA24 /* Component */, 47A1477F2CA15A4E00A91F66 /* OnboardingView.swift */, ); path = Onboarding; @@ -863,6 +868,15 @@ path = UI; sourceTree = ""; }; + 47C815562CE27E050017EA24 /* Component */ = { + isa = PBXGroup; + children = ( + 47C815572CE27E140017EA24 /* OnboardingPageView.swift */, + 47C815592CE27EB00017EA24 /* AppleLoginButton.swift */, + ); + path = Component; + sourceTree = ""; + }; 47D5EDC52CCB66BD00ACA469 /* StudyTimer */ = { isa = PBXGroup; children = ( @@ -1183,6 +1197,7 @@ 47BACCF72CA164BA00295DAC /* Font+.swift in Sources */, 4786A1102CCA351A008635A4 /* WeeklyStudySessionDTO.swift in Sources */, 5E847A742CDBD52E0034C2A7 /* CalendarDataInteractor.swift in Sources */, + 47C8155A2CE27EB00017EA24 /* AppleLoginButton.swift in Sources */, 47C815482CE227E50017EA24 /* ASTokenRequestDTO.swift in Sources */, 470722FA2CBC0A870046469F /* Constants.swift in Sources */, 478F84492CD359260097CAA1 /* WeeklyRanking.swift in Sources */, @@ -1232,6 +1247,7 @@ 470723092CBC198E0046469F /* AuthRepository.swift in Sources */, 47F4F6972CC88FBB00543D24 /* SignUpRequestDTO.swift in Sources */, 478004422CCA924200FFAF00 /* StudySessionMapper.swift in Sources */, + 47C815582CE27E140017EA24 /* OnboardingPageView.swift in Sources */, 478F84372CD30A8F0097CAA1 /* IOSBackground.swift in Sources */, 47A1474E2CA144EC00A91F66 /* StudyRoomUsage+Mock.swift in Sources */, 4774C7BB2CDF632300CDD479 /* WithdrawResponseDTO.swift in Sources */, diff --git a/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift b/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift index 7b23f90..a278829 100644 --- a/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift +++ b/HongikYeolgong2/Domain/Interactors/UserDataInteractor+Migration.swift @@ -188,6 +188,7 @@ final class UserDataMigrationInteractor: UserDataInteractor { .store(in: cancleBag) } + /// 회원 탈퇴 func withdraw() { let clientSecret = makeJWT() diff --git a/HongikYeolgong2/Presentation/Auth/Onboarding/Component/AppleLoginButton.swift b/HongikYeolgong2/Presentation/Auth/Onboarding/Component/AppleLoginButton.swift new file mode 100644 index 0000000..158d66e --- /dev/null +++ b/HongikYeolgong2/Presentation/Auth/Onboarding/Component/AppleLoginButton.swift @@ -0,0 +1,36 @@ +// +// AppleLoginButton.swift +// HongikYeolgong2 +// +// Created by 권석기 on 11/12/24. +// + +import SwiftUI +import AuthenticationServices + +struct AppleLoginButton: View { + let onRequest: (ASAuthorizationAppleIDRequest) -> Void + let onCompletion: (Result) -> Void + + var body: some View { + Button(action: {}) { + Image(.snsLogin) + .resizable() + .frame(maxWidth: .infinity, maxHeight: 52) + } + .padding(.horizontal, 32.adjustToScreenWidth) + .padding(.top, 32.adjustToScreenHeight) + .padding(.bottom, 20.adjustToScreenHeight) + .overlay( + SignInWithAppleButton( + onRequest: onRequest, + onCompletion: onCompletion + ) + .blendMode(.destinationOver) + ) + } +} + +#Preview { + AppleLoginButton(onRequest: {_ in}, onCompletion: {_ in}) +} diff --git a/HongikYeolgong2/Presentation/Auth/Onboarding/Component/OnboardingPageView.swift b/HongikYeolgong2/Presentation/Auth/Onboarding/Component/OnboardingPageView.swift new file mode 100644 index 0000000..855ff56 --- /dev/null +++ b/HongikYeolgong2/Presentation/Auth/Onboarding/Component/OnboardingPageView.swift @@ -0,0 +1,45 @@ +// +// OnboardingPageView.swift +// HongikYeolgong2 +// +// Created by 권석기 on 11/12/24. +// + +import SwiftUI + +struct OnboardingPageView: View { + @Binding var tabIndex: Int + + var body: some View { + VStack { + TabView(selection: $tabIndex) { + Image(.onboarding01) + .tag(0) + Image(.onboarding02) + .tag(1) + Image(.onboarding03) + .tag(2) + } + .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) + + HStack(spacing: 16.adjustToScreenWidth) { + ForEach(0..<3, id: \.self) { index in + Group { + if index == tabIndex { + Image(.shineCount02) + .frame(width: 9, height: 9) + } else { + Circle() + .frame(width: 9, height: 9) + .foregroundColor(.gray600) + } + } + } + } + } + } +} + +#Preview { + OnboardingPageView(tabIndex: .constant(0)) +} diff --git a/HongikYeolgong2/Presentation/Auth/Onboarding/OnboardingView.swift b/HongikYeolgong2/Presentation/Auth/Onboarding/OnboardingView.swift index a91ce85..344acbf 100644 --- a/HongikYeolgong2/Presentation/Auth/Onboarding/OnboardingView.swift +++ b/HongikYeolgong2/Presentation/Auth/Onboarding/OnboardingView.swift @@ -16,81 +16,26 @@ struct OnboardingView: View { // MARK: - Body var body: some View { NavigationStack { - content - .onReceive(routingUpdate) { routingState = $0 } - } - .navigationViewStyle(StackNavigationViewStyle()) - } - - // MARK: - Main Contents - private var content: some View { - VStack { - Spacer() - onboardingPageView - pageIndicator - appleLoginButton - hiddenNavigationLink - } - } - - // MARK: - UI Components - private var onboardingPageView: some View { - TabView(selection: $tabIndex) { - Image(.onboarding01) - .tag(0) - Image(.onboarding02) - .tag(1) - Image(.onboarding03) - .tag(2) - } - .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) - } - - private var pageIndicator: some View { - HStack(spacing: 16.adjustToScreenWidth) { - ForEach(0..<3, id: \.self) { index in - indicatorDot(for: index) - } - } - } - - private func indicatorDot(for index: Int) -> some View { - Group { - if index == tabIndex { - Image(.shineCount02) - .frame(width: 9, height: 9) - } else { - Circle() - .frame(width: 9, height: 9) - .foregroundColor(.gray600) + VStack { + Spacer() + OnboardingPageView(tabIndex: $tabIndex) + + AppleLoginButton( + onRequest: onRequestAppleLogin, + onCompletion: onCompleteAppleLogin + ) + + NavigationLink( + destination: SignUpView(), + isActive: routingBinding.signUp + ) { + EmptyView() + } + .opacity(0) + .frame(width: 0, height: 0) } } - } - - private var appleLoginButton: some View { - Button(action: {}) { - Image(.snsLogin) - .resizable() - .frame(maxWidth: .infinity, maxHeight: 52) - } - .padding(.horizontal, 32.adjustToScreenWidth) - .padding(.top, 32.adjustToScreenHeight) - .padding(.bottom, 20.adjustToScreenHeight) - .overlay( - SignInWithAppleButton( - onRequest: onRequestAppleLogin, - onCompletion: onCompleteAppleLogin - ) - .blendMode(.destinationOver) - ) - } - - private var hiddenNavigationLink: some View { - NavigationLink(destination: SignUpView(), isActive: routingBinding.signUp) { - EmptyView() - } - .opacity(0) - .frame(width: 0, height: 0) + .navigationViewStyle(StackNavigationViewStyle()) } } @@ -123,3 +68,4 @@ extension OnboardingView { var signUp: Bool = false } } + diff --git a/HongikYeolgong2/Util/Services/AppleLoginManager.swift b/HongikYeolgong2/Util/Services/AppleLoginManager.swift index 5ad3031..5adc838 100644 --- a/HongikYeolgong2/Util/Services/AppleLoginManager.swift +++ b/HongikYeolgong2/Util/Services/AppleLoginManager.swift @@ -14,7 +14,7 @@ enum AppleLoginError: Error { protocol AppleLoginService { func requestAppleLogin(_ authrization: ASAuthorization) -> ASAuthorizationAppleIDCredential? - func requestAppleLogin() + func requestAppleLogin() -> AnyPublisher func performExistingAccountSetupFlows() -> AnyPublisher } @@ -32,17 +32,22 @@ final class AppleLoginManager: NSObject, AppleLoginService, ASAuthorizationContr return appleIDCredential } - func requestAppleLogin() { - let appleIDProvider = ASAuthorizationAppleIDProvider() - let request = appleIDProvider.createRequest() - request.requestedScopes = [.fullName, .email] - - let authorizationController = ASAuthorizationController(authorizationRequests: [request]) - - authorizationController.delegate = self - authorizationController.presentationContextProvider = self - - authorizationController.performRequests() + func requestAppleLogin() -> AnyPublisher { + return Future { [weak self] promise in + guard let self = self else { return } + let appleIDProvider = ASAuthorizationAppleIDProvider() + let request = appleIDProvider.createRequest() + request.requestedScopes = [.fullName, .email] + + let authorizationController = ASAuthorizationController(authorizationRequests: [request]) + + appleLoginCompletion = promise + + authorizationController.delegate = self + authorizationController.presentationContextProvider = self + + authorizationController.performRequests() + }.eraseToAnyPublisher() } func performExistingAccountSetupFlows() -> AnyPublisher { From 1b0cf9fd51b1ddd4c8d93ec2eeab4e46c6426f59 Mon Sep 17 00:00:00 2001 From: Seokki-Kwon Date: Tue, 12 Nov 2024 03:25:11 +0900 Subject: [PATCH 9/9] =?UTF-8?q?fix:=20=ED=99=94=EB=A9=B4=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Auth/Onboarding/Component/OnboardingPageView.swift | 1 + .../Presentation/Auth/Onboarding/OnboardingView.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/HongikYeolgong2/Presentation/Auth/Onboarding/Component/OnboardingPageView.swift b/HongikYeolgong2/Presentation/Auth/Onboarding/Component/OnboardingPageView.swift index 855ff56..f41cef7 100644 --- a/HongikYeolgong2/Presentation/Auth/Onboarding/Component/OnboardingPageView.swift +++ b/HongikYeolgong2/Presentation/Auth/Onboarding/Component/OnboardingPageView.swift @@ -36,6 +36,7 @@ struct OnboardingPageView: View { } } } + } } } diff --git a/HongikYeolgong2/Presentation/Auth/Onboarding/OnboardingView.swift b/HongikYeolgong2/Presentation/Auth/Onboarding/OnboardingView.swift index 344acbf..327d21d 100644 --- a/HongikYeolgong2/Presentation/Auth/Onboarding/OnboardingView.swift +++ b/HongikYeolgong2/Presentation/Auth/Onboarding/OnboardingView.swift @@ -34,6 +34,7 @@ struct OnboardingView: View { .opacity(0) .frame(width: 0, height: 0) } + .onReceive(routingUpdate) { routingState = $0 } } .navigationViewStyle(StackNavigationViewStyle()) }