Skip to content

Commit

Permalink
Use strictly-typed result and error types [SDK-2977] (#558)
Browse files Browse the repository at this point in the history
  • Loading branch information
Widcket authored Dec 1, 2021
1 parent 49309e6 commit c08e936
Show file tree
Hide file tree
Showing 25 changed files with 417 additions and 295 deletions.
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

0 comments on commit c08e936

Please sign in to comment.