Revoke access token when removing account

This commit is contained in:
Justin Mazzocchi 2020-08-13 20:40:46 -07:00
parent 4bcb862065
commit fb7d23b5d6
No known key found for this signature in database
GPG Key ID: E223E6937AAFB01C
7 changed files with 68 additions and 15 deletions

View File

@ -44,6 +44,8 @@
D019E6EE24DF7BF300697C7D /* IdentityDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D019E6EC24DF7BF300697C7D /* IdentityDatabase.swift */; };
D019E6F024DF7C2F00697C7D /* DatabaseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D019E6EF24DF7C2F00697C7D /* DatabaseError.swift */; };
D019E6F124DF7C2F00697C7D /* DatabaseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D019E6EF24DF7C2F00697C7D /* DatabaseError.swift */; };
D03DF45B24E62A68007A8CD5 /* DeletionEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03DF45A24E62A68007A8CD5 /* DeletionEndpoint.swift */; };
D03DF45C24E62A68007A8CD5 /* DeletionEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03DF45A24E62A68007A8CD5 /* DeletionEndpoint.swift */; };
D047FAAE24C3E21200AF17C5 /* MetatextApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D047FA8524C3E21000AF17C5 /* MetatextApp.swift */; };
D047FAAF24C3E21200AF17C5 /* MetatextApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D047FA8524C3E21000AF17C5 /* MetatextApp.swift */; };
D047FAB224C3E21200AF17C5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D047FA8724C3E21200AF17C5 /* Assets.xcassets */; };
@ -238,6 +240,7 @@
D019E6E024DF72E700697C7D /* InstanceEndpoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstanceEndpoint.swift; sourceTree = "<group>"; };
D019E6EC24DF7BF300697C7D /* IdentityDatabase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IdentityDatabase.swift; sourceTree = "<group>"; };
D019E6EF24DF7C2F00697C7D /* DatabaseError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseError.swift; sourceTree = "<group>"; };
D03DF45A24E62A68007A8CD5 /* DeletionEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletionEndpoint.swift; sourceTree = "<group>"; };
D047FA8524C3E21000AF17C5 /* MetatextApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetatextApp.swift; sourceTree = "<group>"; };
D047FA8724C3E21200AF17C5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
D047FA8C24C3E21200AF17C5 /* Metatext.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Metatext.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -396,6 +399,7 @@
D019E6DF24DF72E700697C7D /* AccessTokenEndpoint.swift */,
D019E6DE24DF72E700697C7D /* AccountEndpoint.swift */,
D019E6DC24DF72E700697C7D /* AppAuthorizationEndpoint.swift */,
D03DF45A24E62A68007A8CD5 /* DeletionEndpoint.swift */,
D019E6E024DF72E700697C7D /* InstanceEndpoint.swift */,
D019E6DD24DF72E700697C7D /* PreferencesEndpoint.swift */,
D0EC8DED24E2704D00A08489 /* PushSubscriptionEndpoint.swift */,
@ -934,6 +938,7 @@
D0091B6E24DD68090040E8D2 /* PreferencesView.swift in Sources */,
D0159F8F24DE743700E78478 /* IdentitiesView.swift in Sources */,
D0EC8DEB24E26F1100A08489 /* PushSubscription.swift in Sources */,
D03DF45B24E62A68007A8CD5 /* DeletionEndpoint.swift in Sources */,
D0DB6EF424C5228A00D965FE /* AddIdentityView.swift in Sources */,
D074577724D29006004758DB /* MockWebAuthSession.swift in Sources */,
D0159FA524DE989700E78478 /* NSMutableAttributedString+Extensions.swift in Sources */,
@ -1020,6 +1025,7 @@
D074577B24D29366004758DB /* URLSessionConfiguration+Extensions.swift in Sources */,
D0ED1BB824CE47F400B4899C /* WebAuthSession.swift in Sources */,
D0EC8DEC24E26F1100A08489 /* PushSubscription.swift in Sources */,
D03DF45C24E62A68007A8CD5 /* DeletionEndpoint.swift in Sources */,
D0A1CA7524DAC2F1003063E9 /* KingfisherOptionsInfo+Extensions.swift in Sources */,
D0ED1BC524CED54D00B4899C /* HTTPTarget.swift in Sources */,
D0C963FF24CC3812003BD330 /* Publisher+Extensions.swift in Sources */,

View File

@ -0,0 +1,39 @@
// Copyright © 2020 Metabolist. All rights reserved.
import Foundation
enum DeletionEndpoint {
case oauthRevoke(token: String, clientID: String, clientSecret: String)
}
extension DeletionEndpoint: MastodonEndpoint {
typealias ResultType = [String: String]
var context: [String] {
switch self {
case .oauthRevoke:
return []
}
}
var pathComponentsInContext: [String] {
switch self {
case .oauthRevoke:
return ["oauth", "revoke"]
}
}
var method: HTTPMethod {
switch self {
case .oauthRevoke:
return .post
}
}
var parameters: [String: Any]? {
switch self {
case let .oauthRevoke(token, clientID, clientSecret):
return ["token": token, "client_id": clientID, "client_secret": clientSecret]
}
}
}

View File

@ -50,18 +50,22 @@ extension IdentitiesService {
.eraseToAnyPublisher()
}
func deleteIdentity(id: UUID) -> AnyPublisher<Void, Error> {
let environment = self.environment
func deleteIdentity(_ identity: Identity) -> AnyPublisher<Void, Error> {
let secretsService = SecretsService(identityID: identity.id, keychainService: environment.keychainServiceType)
let networkClient = MastodonClient(session: environment.session)
return identityDatabase.deleteIdentity(id: id)
.tryMap { _ -> Void in
try SecretsService(
identityID: id,
keychainService: environment.keychainServiceType)
.deleteAllItems()
networkClient.instanceURL = identity.url
return ()
return identityDatabase.deleteIdentity(id: identity.id)
.tryMap {
DeletionEndpoint.oauthRevoke(
token: try secretsService.item(.accessToken),
clientID: try secretsService.item(.clientID),
clientSecret: try secretsService.item(.clientSecret))
}
.flatMap(networkClient.request)
.tryMap { _ in try secretsService.deleteAllItems() }
.print()
.eraseToAnyPublisher()
}

View File

@ -35,7 +35,7 @@ class IdentityService {
keychainService: environment.keychainServiceType)
networkClient = MastodonClient(session: environment.session)
networkClient.instanceURL = identity.url
networkClient.accessToken = try secretsService.item(.accessToken)
networkClient.accessToken = try? secretsService.item(.accessToken)
observation.catch { [weak self] error -> Empty<Identity, Never> in
self?.observationErrorsInput.send(error)

View File

@ -31,6 +31,10 @@ extension SecretsService {
}
}
enum SecretsServiceError: Error {
case itemAbsent
}
extension SecretsService.Item {
enum Kind {
case genericPassword
@ -53,11 +57,11 @@ extension SecretsService {
service: Self.keychainServiceName)
}
func item<T: SecretsStorable>(_ item: Item) throws -> T? {
func item<T: SecretsStorable>(_ item: Item) throws -> T {
guard let data = try keychainService.getGenericPassword(
account: key(item: item),
service: Self.keychainServiceName) else {
return nil
throw SecretsServiceError.itemAbsent
}
return try T.fromDataStoredInSecrets(data)

View File

@ -70,8 +70,8 @@ extension RootViewModel {
mainNavigationViewModel = MainNavigationViewModel(identityService: identityService)
}
func deleteIdentity(id: UUID) {
identitiesService.deleteIdentity(id: id)
func deleteIdentity(_ identity: Identity) {
identitiesService.deleteIdentity(identity)
.sink { _ in } receiveValue: { _ in }
.store(in: &cancellables)
}

View File

@ -53,7 +53,7 @@ struct IdentitiesView: View {
.onDelete {
guard let index = $0.first else { return }
rootViewModel.deleteIdentity(id: viewModel.identities[index].id)
rootViewModel.deleteIdentity(viewModel.identities[index])
}
}
}