diff --git a/MastodonKit/Sources/MastodonKit/MastodonClient+Convenience.swift b/MastodonKit/Sources/MastodonKit/MastodonClient+Convenience.swift index 5d10196..dcf046e 100644 --- a/MastodonKit/Sources/MastodonKit/MastodonClient+Convenience.swift +++ b/MastodonKit/Sources/MastodonKit/MastodonClient+Convenience.swift @@ -43,7 +43,7 @@ public extension MastodonClient { self?.oAuthContinuation = continuation oauthClient?.renewAccessToken( - withRefreshToken: "refrestoken", + withRefreshToken: refreshToken, completionHandler: { result in switch result { case let .success((credentials, _, _)): diff --git a/Vernissage.xcodeproj/project.pbxproj b/Vernissage.xcodeproj/project.pbxproj index 3c0a258..11b26c8 100644 --- a/Vernissage.xcodeproj/project.pbxproj +++ b/Vernissage.xcodeproj/project.pbxproj @@ -234,6 +234,7 @@ F89D6C4329718092001DA3D4 /* AccentsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccentsSection.swift; sourceTree = ""; }; F89D6C4529718193001DA3D4 /* ThemeSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeSection.swift; sourceTree = ""; }; F89D6C49297196FF001DA3D4 /* ImagesViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagesViewer.swift; sourceTree = ""; }; + F89F0605299139F6003DC875 /* Vernissage-002.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Vernissage-002.xcdatamodel"; sourceTree = ""; }; F8A93D7D2965FD89001D8331 /* UserProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileView.swift; sourceTree = ""; }; F8B1E6502973FB7E00EE0D10 /* ToastrService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastrService.swift; sourceTree = ""; }; F8B9B344298D1FCB009CC69C /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = ""; }; @@ -1071,10 +1072,11 @@ F88C2476295C37BB0006098B /* Vernissage.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + F89F0605299139F6003DC875 /* Vernissage-002.xcdatamodel */, F8C937A929882CA90004D782 /* Vernissage-001.xcdatamodel */, F88C2477295C37BB0006098B /* Vernissage.xcdatamodel */, ); - currentVersion = F8C937A929882CA90004D782 /* Vernissage-001.xcdatamodel */; + currentVersion = F89F0605299139F6003DC875 /* Vernissage-002.xcdatamodel */; path = Vernissage.xcdatamodeld; sourceTree = ""; versionGroupType = wrapper.xcdatamodel; diff --git a/Vernissage/CoreData/ApplicationSettings+CoreDataProperties.swift b/Vernissage/CoreData/ApplicationSettings+CoreDataProperties.swift index 52bf840..5efe575 100644 --- a/Vernissage/CoreData/ApplicationSettings+CoreDataProperties.swift +++ b/Vernissage/CoreData/ApplicationSettings+CoreDataProperties.swift @@ -17,7 +17,7 @@ extension ApplicationSettings { @NSManaged public var theme: Int32 @NSManaged public var tintColor: Int32 @NSManaged public var avatarShape: Int32 - + @NSManaged public var lastRefreshTokens: Date } extension ApplicationSettings : Identifiable { diff --git a/Vernissage/Services/AuthorizationService.swift b/Vernissage/Services/AuthorizationService.swift index 262c060..28e4513 100644 --- a/Vernissage/Services/AuthorizationService.swift +++ b/Vernissage/Services/AuthorizationService.swift @@ -56,7 +56,7 @@ public class AuthorizationService { // Verify address. _ = try await client.readInstanceInformation() - // Create application (we will get clientId amd clientSecret). + // Create application (we will get clientId and clientSecret). let oAuthApp = try await client.createApp( named: AppConstants.oauthApplicationName, redirectUri: AppConstants.oauthRedirectUri, @@ -76,8 +76,8 @@ public class AuthorizationService { // Get account information from server. let account = try await authenticatedClient.verifyCredentials() - // Create account object in database. - let accountData = AccountDataHandler.shared.createAccountDataEntity() + // Get/create account object in database. + let accountData = self.getAccountData(account: account) accountData.id = account.id accountData.username = account.username @@ -125,6 +125,44 @@ public class AuthorizationService { result(accountData) } + public func refreshAccessTokens() async { + let accounts = AccountDataHandler.shared.getAccountsData() + + await withTaskGroup(of: Void.self) { group in + for account in accounts { + group.addTask { + do { + try await self.refreshAccessToken(accountData: account) + } catch { + ErrorService.shared.handle(error, message: "Error during refreshing access token for account '\(account.acct)'.") + } + } + } + } + } + + private func refreshAccessToken(accountData: AccountData) async throws { + let client = MastodonClient(baseURL: accountData.serverUrl) + + guard let refreshToken = accountData.refreshToken else { + return + } + + let oAuthSwiftCredential = try await client.refreshToken(clientId: accountData.clientId, + clientSecret: accountData.clientSecret, + refreshToken: refreshToken) + + // Get authenticated client. + let authenticatedClient = client.getAuthenticated(token: oAuthSwiftCredential.oauthToken) + + // Get account information from server. + let account = try await authenticatedClient.verifyCredentials() + try await self.update(account: accountData, + basedOn: account, + accessToken: oAuthSwiftCredential.oauthToken, + refreshToken: oAuthSwiftCredential.oauthRefreshToken) + } + private func refreshCredentials(for accountData: AccountData, presentationContextProvider: ASWebAuthenticationPresentationContextProviding ) async throws { @@ -191,4 +229,12 @@ public class AuthorizationService { // Save account data in database and in application state. CoreDataHandler.shared.save() } + + private func getAccountData(account: Account) -> AccountData { + if let accountFromDb = AccountDataHandler.shared.getAccountData(accountId: account.id) { + return accountFromDb + } + + return AccountDataHandler.shared.createAccountDataEntity() + } } diff --git a/Vernissage/Vernissage.xcdatamodeld/.xccurrentversion b/Vernissage/Vernissage.xcdatamodeld/.xccurrentversion index ad1d243..17d88c3 100644 --- a/Vernissage/Vernissage.xcdatamodeld/.xccurrentversion +++ b/Vernissage/Vernissage.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - Vernissage-001.xcdatamodel + Vernissage-002.xcdatamodel diff --git a/Vernissage/Vernissage.xcdatamodeld/Vernissage-002.xcdatamodel/contents b/Vernissage/Vernissage.xcdatamodeld/Vernissage-002.xcdatamodel/contents new file mode 100644 index 0000000..4cac02a --- /dev/null +++ b/Vernissage/Vernissage.xcdatamodeld/Vernissage-002.xcdatamodel/contents @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Vernissage/VernissageApp.swift b/Vernissage/VernissageApp.swift index 5f42714..0bbdd9a 100644 --- a/Vernissage/VernissageApp.swift +++ b/Vernissage/VernissageApp.swift @@ -58,6 +58,9 @@ struct VernissageApp: App { // Load user preferences from database. self.loadUserPreferences() + // Refresh other access tokens. + await self.refreshAccessTokens() + // Verify access token correctness. let authorizationSession = AuthorizationSession() await AuthorizationService.shared.verifyAccount(session: authorizationSession) { accountData in @@ -124,6 +127,23 @@ struct VernissageApp: App { ImagePipeline.shared = pipeline } + + private func refreshAccessTokens() async { + let defaultSettings = ApplicationSettingsHandler.shared.getDefaultSettings() + print(defaultSettings.lastRefreshTokens) + + // Run refreshing access tokens once per day. + guard let refreshTokenDate = Calendar.current.date(byAdding: .day, value: 1, to: defaultSettings.lastRefreshTokens), refreshTokenDate < Date.now else { + return + } + + // Refresh access tokens. + await AuthorizationService.shared.refreshAccessTokens() + + // Update time when refresh tokens has been updated. + defaultSettings.lastRefreshTokens = Date.now + CoreDataHandler.shared.save() + } } class AppDelegate: NSObject, UIApplicationDelegate {