Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use strictly-typed result and error types [SDK-2977] #558

Merged
merged 4 commits into from
Dec 1, 2021
Merged
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
2 changes: 1 addition & 1 deletion App/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Auth0

class ViewController: UIViewController {

var onAuth: ((Auth0Result<Credentials>) -> ())!
var onAuth: ((WebAuthResult<Credentials>) -> ())!

override func viewDidLoad() {
super.viewDidLoad()
Expand Down
5 changes: 2 additions & 3 deletions Auth0/ASTransaction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@ final class ASTransaction: BaseTransaction {
let authSession = ASWebAuthenticationSession(url: authorizeURL,
callbackURLScheme: self.redirectURL.scheme) { [weak self] in
guard $1 == nil, let callbackURL = $0 else {
let authError = $1 ?? WebAuthError(code: .unknown("ASWebAuthenticationSession failed"))
if case ASWebAuthenticationSessionError.canceledLogin = authError {
if let authError = $1, case ASWebAuthenticationSessionError.canceledLogin = authError {
self?.callback(.failure(WebAuthError(code: .userCancelled)))
} else {
self?.callback(.failure(authError))
self?.callback(.failure(WebAuthError(code: .unknown("ASWebAuthenticationSession failed"))))
}
return TransactionStore.shared.clear()
}
Expand Down
21 changes: 19 additions & 2 deletions Auth0/Auth0.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
import Foundation

/**
Auth0.swift wrapper for the Swift `Result` type
`Result` wrapper for Authentication API operations
*/
public typealias Auth0Result<T> = Result<T, Error>
public typealias AuthenticationResult<T> = Result<T, AuthenticationError>

/**
`Result` wrapper for Management API operations
*/
public typealias ManagementResult<T> = Result<T, ManagementError>

#if WEB_AUTH_PLATFORM
/**
`Result` wrapper for Web Auth operations
*/
public typealias WebAuthResult<T> = Result<T, WebAuthError>
#endif

/**
`Result` wrapper for Credentials Manager operations
*/
public typealias CredentialsManagerResult<T> = Result<T, CredentialsManagerError>

/**
Default scope value used across Auth0.swift
Expand Down
15 changes: 14 additions & 1 deletion Auth0/Auth0Error.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,24 @@ public protocol Auth0APIError: Auth0Error {
*/
init(info: [String: Any], statusCode: Int?)

/**
Returns a value from the error data

- parameter key: key of the value to return

- returns: the value of key or nil if cannot be found or is of the wrong type.
*/
subscript<T>(_ key: String) -> T? { get }

}

extension Auth0APIError {

init(error: Error, statusCode: Int? = 0) {
init(info: [String: Any], statusCode: Int? = 0) {
self.init(info: info, statusCode: statusCode)
}

init(cause error: Error, statusCode: Int? = 0) {
let info: [String: Any] = [
"code": nonJSONError,
"description": error.localizedDescription,
Expand Down
4 changes: 2 additions & 2 deletions Auth0/Auth0WebAuth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ final class Auth0WebAuth: WebAuth {
self.session = session
self.storage = storage
self.telemetry = telemetry
self.issuer = "\(url.absoluteString)/"
self.issuer = url.absoluteString
}

func connection(_ connection: String) -> Self {
Expand Down Expand Up @@ -121,7 +121,7 @@ final class Auth0WebAuth: WebAuth {
return self
}

func start(_ callback: @escaping (Auth0Result<Credentials>) -> Void) {
func start(_ callback: @escaping (WebAuthResult<Credentials>) -> Void) {
guard let redirectURL = self.redirectURL else {
return callback(.failure(WebAuthError(code: .noBundleIdentifier)))
}
Expand Down
2 changes: 1 addition & 1 deletion Auth0/AuthenticationError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ extension AuthenticationError {

- returns: the value of key or nil if cannot be found or is of the wrong type.
*/
subscript<T>(_ key: String) -> T? {
public subscript<T>(_ key: String) -> T? {
return self.info[key] as? T
}

Expand Down
7 changes: 4 additions & 3 deletions Auth0/BaseTransaction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Foundation

class BaseTransaction: NSObject, AuthTransaction {

typealias FinishTransaction = (Auth0Result<Credentials>) -> Void
typealias FinishTransaction = (WebAuthResult<Credentials>) -> Void

var authSession: AuthSession?
let state: String?
Expand Down Expand Up @@ -44,13 +44,14 @@ class BaseTransaction: NSObject, AuthTransaction {
private func handleURL(_ url: URL) -> Bool {
guard url.absoluteString.lowercased().hasPrefix(self.redirectURL.absoluteString.lowercased()) else { return false }
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
self.callback(.failure(AuthenticationError(description: url.absoluteString, statusCode: 200)))
let error = WebAuthError(code: .unknown("Invalid callback URL: \(url.absoluteString)"))
self.callback(.failure(error))
return false
}
let items = self.handler.values(fromComponents: components)
guard has(state: self.state, inItems: items) else { return false }
if items["error"] != nil {
self.callback(.failure(AuthenticationError(info: items, statusCode: 0)))
self.callback(.failure(WebAuthError(code: .other, cause: AuthenticationError(info: items))))
} else {
self.handler.credentials(from: items, callback: self.callback)
}
Expand Down
26 changes: 13 additions & 13 deletions Auth0/CredentialsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,17 +162,17 @@ public struct CredentialsManager {
/// - Important: This method only works for a refresh token obtained after auth with OAuth 2.0 API Authorization.
/// - Note: [Auth0 Refresh Tokens Docs](https://auth0.com/docs/tokens/concepts/refresh-tokens)
#if WEB_AUTH_PLATFORM
public func credentials(withScope scope: String? = nil, minTTL: Int = 0, parameters: [String: Any] = [:], callback: @escaping (CredentialsManagerError?, Credentials?) -> Void) {
guard self.hasValid(minTTL: minTTL) else { return callback(.noCredentials, nil) }
public func credentials(withScope scope: String? = nil, minTTL: Int = 0, parameters: [String: Any] = [:], callback: @escaping (CredentialsManagerResult<Credentials>) -> Void) {
guard self.hasValid(minTTL: minTTL) else { return callback(.failure(.noCredentials)) }
if let bioAuth = self.bioAuth {
guard bioAuth.available else {
let error = CredentialsManagerError(code: .biometricsFailed,
cause: LAError(LAError.biometryNotAvailable))
return callback(error, nil)
return callback(.failure(error))
}
bioAuth.validateBiometric {
guard $0 == nil else {
return callback(CredentialsManagerError(code: .biometricsFailed, cause: $0!), nil)
return callback(.failure(CredentialsManagerError(code: .biometricsFailed, cause: $0!)))
}
self.retrieveCredentials(withScope: scope, minTTL: minTTL, parameters: parameters, callback: callback)
}
Expand All @@ -181,8 +181,8 @@ public struct CredentialsManager {
}
}
#else
public func credentials(withScope scope: String? = nil, minTTL: Int = 0, parameters: [String: Any] = [:], callback: @escaping (CredentialsManagerError?, Credentials?) -> Void) {
guard self.hasValid(minTTL: minTTL) else { return callback(.noCredentials, nil) }
public func credentials(withScope scope: String? = nil, minTTL: Int = 0, parameters: [String: Any] = [:], callback: @escaping (CredentialsManagerResult<Credentials>) -> Void) {
guard self.hasValid(minTTL: minTTL) else { return callback(.failure(.noCredentials)) }
self.retrieveCredentials(withScope: scope, minTTL: minTTL, parameters: parameters, callback: callback)
}
#endif
Expand All @@ -194,24 +194,24 @@ public struct CredentialsManager {
return credentials
}

private func retrieveCredentials(withScope scope: String?, minTTL: Int, parameters: [String: Any] = [:], callback: @escaping (CredentialsManagerError?, Credentials?) -> Void) {
private func retrieveCredentials(withScope scope: String?, minTTL: Int, parameters: [String: Any] = [:], callback: @escaping (CredentialsManagerResult<Credentials>) -> Void) {
self.dispatchQueue.async {
self.dispatchGroup.enter()

DispatchQueue.global(qos: .userInitiated).async {
guard let credentials = retrieveCredentials() else {
self.dispatchGroup.leave()
return callback(.noCredentials, nil)
return callback(.failure(.noCredentials))
}
guard self.hasExpired(credentials) ||
self.willExpire(credentials, within: minTTL) ||
self.hasScopeChanged(credentials, from: scope) else {
self.dispatchGroup.leave()
return callback(nil, credentials)
return callback(.success(credentials))
}
guard let refreshToken = credentials.refreshToken else {
self.dispatchGroup.leave()
return callback(.noRefreshToken, nil)
return callback(.failure(.noRefreshToken))
}
self.authentication
.renew(withRefreshToken: refreshToken, scope: scope)
Expand All @@ -229,15 +229,15 @@ public struct CredentialsManager {
let tokenLifetime = Int(credentials.expiresIn.timeIntervalSinceNow)
let error = CredentialsManagerError(code: .largeMinTTL(minTTL: minTTL, lifetime: tokenLifetime))
self.dispatchGroup.leave()
callback(error, nil)
callback(.failure(error))
} else {
_ = self.store(credentials: newCredentials)
self.dispatchGroup.leave()
callback(nil, newCredentials)
callback(.success(newCredentials))
}
case .failure(let error):
self.dispatchGroup.leave()
callback(CredentialsManagerError(code: .refreshFailed, cause: error), nil)
callback(.failure(CredentialsManagerError(code: .refreshFailed, cause: error)))
}
}
}
Expand Down
26 changes: 16 additions & 10 deletions Auth0/Handlers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ func plainJson(from response: Response<AuthenticationError>, callback: Request<[
} else {
callback(.failure(AuthenticationError(from: response)))
}

} catch let error {
} catch let error as AuthenticationError {
callback(.failure(error))
} catch {
callback(.failure(AuthenticationError(cause: error)))
}
}

Expand All @@ -24,9 +25,10 @@ func codable<T: Codable>(from response: Response<AuthenticationError>, callback:
} else {
callback(.failure(AuthenticationError(from: response)))
}

} catch let error {
} catch let error as AuthenticationError {
callback(.failure(error))
} catch {
callback(.failure(AuthenticationError(cause: error)))
}
}

Expand All @@ -37,9 +39,10 @@ func authenticationObject<T: JSONObjectPayload>(from response: Response<Authenti
} else {
callback(.failure(AuthenticationError(from: response)))
}

} catch let error {
} catch let error as AuthenticationError {
callback(.failure(error))
} catch {
callback(.failure(AuthenticationError(cause: error)))
}
}

Expand All @@ -52,19 +55,22 @@ func databaseUser(from response: Response<AuthenticationError>, callback: Reques
} else {
callback(.failure(AuthenticationError(from: response)))
}

} catch let error {
} catch let error as AuthenticationError {
callback(.failure(error))
} catch {
callback(.failure(AuthenticationError(cause: error)))
}
}

func noBody(from response: Response<AuthenticationError>, callback: Request<Void, AuthenticationError>.Callback) {
do {
_ = try response.result()
callback(.success(()))
} catch let error as Auth0APIError where error.code == emptyBodyError {
} catch let error as AuthenticationError where error.code == emptyBodyError {
callback(.success(()))
} catch let error {
} catch let error as AuthenticationError {
callback(.failure(error))
} catch {
callback(.failure(AuthenticationError(cause: error)))
}
}
8 changes: 6 additions & 2 deletions Auth0/Management.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ struct Management: Trackable, Loggable {
} else {
callback(.failure(ManagementError(from: response)))
}
} catch let error {
} catch let error as ManagementError {
callback(.failure(error))
} catch {
callback(.failure(ManagementError(cause: error)))
}
}

Expand All @@ -45,8 +47,10 @@ struct Management: Trackable, Loggable {
} else {
callback(.failure(ManagementError(from: response)))
}
} catch let error {
} catch let error as ManagementError {
callback(.failure(error))
} catch {
callback(.failure(ManagementError(cause: error)))
}
}
}
2 changes: 1 addition & 1 deletion Auth0/ManagementError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ extension ManagementError {

- returns: the value of key or nil if cannot be found or is of the wrong type.
*/
subscript<T>(_ key: String) -> T? {
public subscript<T>(_ key: String) -> T? {
return self.info[key] as? T
}

Expand Down
11 changes: 5 additions & 6 deletions Auth0/OAuth2Grant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Foundation

protocol OAuth2Grant {
var defaults: [String: String] { get }
func credentials(from values: [String: String], callback: @escaping (Auth0Result<Credentials>) -> Void)
func credentials(from values: [String: String], callback: @escaping (WebAuthResult<Credentials>) -> Void)
func values(fromComponents components: URLComponents) -> [String: String]
}

Expand Down Expand Up @@ -62,10 +62,9 @@ struct PKCE: OAuth2Grant {
self.defaults = newDefaults
}

func credentials(from values: [String: String], callback: @escaping (Auth0Result<Credentials>) -> Void) {
func credentials(from values: [String: String], callback: @escaping (WebAuthResult<Credentials>) -> Void) {
guard let code = values["code"] else {
let string = "No code found in parameters \(values)"
return callback(.failure(AuthenticationError(description: string)))
return callback(.failure(WebAuthError(code: .noAuthorizationCode(values))))
}
let authentication = self.authentication
let verifier = self.verifier
Expand All @@ -80,7 +79,7 @@ struct PKCE: OAuth2Grant {
.tokenExchange(withCode: code, codeVerifier: verifier, redirectURI: redirectUrlString)
.start { result in
switch result {
case .failure(let error as AuthenticationError) where error.localizedDescription == "Unauthorized":
case .failure(let error) where error.localizedDescription == "Unauthorized":
// Special case for PKCE when the correct method for token endpoint authentication is not set (it should be None)
return callback(.failure(WebAuthError(code: .pkceNotAllowed)))
case .failure(let error): return callback(.failure(WebAuthError(code: .other, cause: error)))
Expand All @@ -89,7 +88,7 @@ struct PKCE: OAuth2Grant {
if let error = error {
return callback(.failure(WebAuthError(code: .idTokenValidationFailed, cause: error)))
}
callback(result)
callback(.success(credentials))
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Auth0/Request.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Foundation
```
*/
public struct Request<T, E: Auth0APIError>: Requestable {
public typealias Callback = (Auth0Result<T>) -> Void
public typealias Callback = (Result<T, E>) -> Void

let session: URLSession
let url: URL
Expand Down
3 changes: 2 additions & 1 deletion Auth0/Requestable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Foundation

protocol Requestable {
associatedtype ResultType
associatedtype ErrorType: Auth0APIError

func start(_ callback: @escaping (Auth0Result<ResultType>) -> Void)
func start(_ callback: @escaping (Result<ResultType, ErrorType>) -> Void)
}
2 changes: 1 addition & 1 deletion Auth0/Response.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct Response<E: Auth0APIError> {
let error: Error?

func result() throws -> Any? {
guard error == nil else { throw E(error: error!, statusCode: response?.statusCode) }
guard error == nil else { throw E(cause: error!, statusCode: response?.statusCode) }
guard let response = self.response else { throw E(description: nil) }
guard (200...300).contains(response.statusCode) else {
if let json = json(data) as? [String: Any] {
Expand Down
2 changes: 1 addition & 1 deletion Auth0/WebAuth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ public protocol WebAuth: Trackable, Loggable {

- parameter callback: callback called with the result of the WebAuth flow
*/
func start(_ callback: @escaping (Auth0Result<Credentials>) -> Void)
func start(_ callback: @escaping (WebAuthResult<Credentials>) -> Void)

/**
Removes Auth0 session and optionally remove the Identity Provider session.
Expand Down
Loading