Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let package = Package(
targets: ["TIM"]),
],
dependencies: [
.package(name: "AppAuth", url: "https://github.com/openid/AppAuth-iOS", .exact("1.4.0")),
.package(name: "AppAuth", url: "https://github.com/openid/AppAuth-iOS", .exact("1.6.0")),
.package(name: "TIMEncryptedStorage", url: "https://github.com/trifork/TIMEncryptedStorage-iOS", .exact("2.2.1")),

],
Expand Down
3 changes: 1 addition & 2 deletions Sources/TIM/AppAuth/AppAuthController.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import UIKit
import AppAuth
import SafariServices

Expand Down Expand Up @@ -177,7 +176,7 @@ public final class AppAuthController: OpenIDConnectController {

public func accessToken(forceRefresh: Bool, _ completion: @escaping (Result<JWT, TIMAuthError>) -> Void) {
guard let authState = self.authState else {
completion(.failure(TIMAuthError.authStateNil))
completion(.failure(TIMAuthError.authStateNil()))
return
}
if forceRefresh {
Expand Down
159 changes: 0 additions & 159 deletions Sources/TIM/Models/Errors.swift

This file was deleted.

77 changes: 77 additions & 0 deletions Sources/TIM/Models/TIMAuthError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import Foundation
import AppAuth
import TIMEncryptedStorage

/// Errors related to AppAuth operations
public enum TIMAuthError: Error, LocalizedError {
//TODO consider renaming this to "missingLogin" or alike which communicates the real issue.
case authStateNil(stacktrace: [String] = Thread.callStackSymbols)
case failedToDiscoverConfiguration
case failedToBeginAuth
case failedToGetAccessToken
case failedToGetRefreshToken
case networkError
case refreshTokenExpired
case appAuthFailed(Error?)
case safariViewControllerCancelled
case failedToGetRequiredDataInToken
case failedToValidateIDToken

public var errorDescription: String? {
switch self {
case .authStateNil(let stacktrace):
return "The AuthState was nil, when it wasn't expected to be. Are you trying to get the access token, when there was no valid session? (stacktrace: \(stacktrace))"
case .failedToDiscoverConfiguration:
return "Failed to discover the configuration on the server. Check your configuration setup and try again."
case .failedToBeginAuth:
return "AppAuth returned a weird state, while we tried to begin the authentication."
case .failedToGetAccessToken:
return "Failed to get the access token."
case .failedToGetRefreshToken:
return "Failed to get the refresh token."
case .networkError:
return "Network error caused by AppAuth"
case .refreshTokenExpired:
return "The refresh token has expired."
case .appAuthFailed(let error):
return "Something went wrong in the AppAuth framework: \(error?.localizedDescription ?? "nil")"
case .safariViewControllerCancelled:
return "The user cancelled OpenID connect login via SafariViewController"
case .failedToGetRequiredDataInToken:
return "TIM did not find the required data (userId) in the token. The 'sub' property must be present in the token!"
case .failedToValidateIDToken:
return "AppAuth failed to validate the ID Token. This will happen if the client's time is more than 10 minutes off the current time."
}
}

static func mapAppAuthError(_ error: Error?) -> TIMAuthError {
guard let error = error as NSError? else {
return .appAuthFailed(nil)
}

switch error.domain {
case OIDGeneralErrorDomain:
switch error.code {
case OIDErrorCode.networkError.rawValue: return .networkError
case OIDErrorCode.idTokenFailedValidationError.rawValue: return .failedToValidateIDToken
default: break
}
case OIDOAuthTokenErrorDomain:
switch error.code {
case OIDErrorCodeOAuth.invalidGrant.rawValue: return .refreshTokenExpired
default: break
}
default: break
}

return .appAuthFailed(error)
}

/// Tells whether the error was a `.safariViewControllerCancelled` or not.
public func isSafariViewControllerCancelled() -> Bool {
if case .safariViewControllerCancelled = self {
return true
}
return false
}
}
71 changes: 71 additions & 0 deletions Sources/TIM/Models/TIMStorageError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import Foundation
import AppAuth
import TIMEncryptedStorage

/// Errors related to storage operations
public enum TIMStorageError: Error, LocalizedError {
case encryptedStorageFailed(TIMEncryptedStorageError)

// This error was implemented because we discovered missing keyIds for some users, where the user was cleared while waiting for response with a new refreshToken
case incompleteUserDataSet

public var errorDescription: String? {
switch self {
case .encryptedStorageFailed(let error):
return "The encrypted storage failed: \(error.localizedDescription)"
case .incompleteUserDataSet:
return "Attempt to store a refresh token for a user, that does not have a valid data set. This can happen if you clear the user data while waiting for a login (which definitely should be avoided!). The invalid data has now been cleared from the framework. The user will have to perform OIDC login again."
}
}

public func isKeyLocked() -> Bool {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jeg ville bruge computer propperties her i stedet

isKeyServiceError(.keyLocked)
}

public func isWrongPassword() -> Bool {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jeg ville bruge computer propperties her i stedet

isKeyServiceError(.badPassword)
}

public func isBiometricFailedError() -> Bool {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jeg ville bruge computer propperties her i stedet

switch self {
case .encryptedStorageFailed(let storageError):
switch storageError {
case .secureStorageFailed(let secureStorageError) where secureStorageError == .authenticationFailedForData:
return true
default:
return false
}
case .incompleteUserDataSet:
return false
}
}

/// Determines whether this error is an error thrown by the KeyService.
///
/// This might be useful for handling unexpected cases from the encryption part of the framework.
/// When the key service fails you don't want to do any drastic fallback, since the server might "just" be down or the user have no internet connection. You will be able to recover later on, from a key service error.
public func isKeyServiceError() -> Bool {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jeg ville bruge computer propperties her i stedet

isKeyServiceError(nil)
}

/// Determines whether this error is a specific kind of key service error.
/// - Parameter keyServiceError: The key service error to look for. If `nil` is passed it will look for any kind of key service error.
private func isKeyServiceError(_ keyServiceError: TIMKeyServiceError?) -> Bool {
let isKeyServiceError: Bool
switch self {
case .encryptedStorageFailed(let error):
if case TIMEncryptedStorageError.keyServiceFailed(let ksError) = error {
if let keyServiceError = keyServiceError {
isKeyServiceError = ksError == keyServiceError
} else {
isKeyServiceError = true // Is any kind of key service error!
}
} else {
isKeyServiceError = false
}
case .incompleteUserDataSet:
isKeyServiceError = false
}
return isKeyServiceError
}
}
18 changes: 18 additions & 0 deletions Sources/TIM/Models/TimError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Foundation
import AppAuth
import TIMEncryptedStorage

/// Shared wrapper for Auth and Storage errors.
public enum TIMError: Error, LocalizedError {
case auth(TIMAuthError)
case storage(TIMStorageError)

public var errorDescription: String? {
switch self {
case .auth(let error):
return error.localizedDescription
case .storage(let error):
return error.localizedDescription
}
}
}
Loading