Skip to content

Commit

Permalink
Use strongly-typed result and error types
Browse files Browse the repository at this point in the history
  • Loading branch information
Widcket committed Nov 30, 2021
1 parent 1931514 commit aedd067
Show file tree
Hide file tree
Showing 27 changed files with 490 additions and 311 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
29 changes: 23 additions & 6 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 All @@ -19,7 +36,7 @@ public let defaultScope = "openid profile email"

- parameter clientId: clientId of your Auth0 application
- parameter domain: domain of your Auth0 account. e.g.: 'samples.auth0.com'
- parameter session: instance of NSURLSession used for networking. By default it will use the shared NSURLSession
- parameter session: instance of URLSession used for networking. By default it will use the shared URLSession

- returns: Auth0 Authentication API
*/
Expand Down Expand Up @@ -49,7 +66,7 @@ public func authentication(clientId: String, domain: String, session: URLSession
</plist>
```

- parameter session: instance of NSURLSession used for networking. By default it will use the shared NSURLSession
- parameter session: instance of URLSession used for networking. By default it will use the shared URLSession
- parameter bundle: bundle used to locate the `Auth0.plist` file. By default is the main bundle

- returns: Auth0 Authentication API
Expand Down Expand Up @@ -90,7 +107,7 @@ public func authentication(session: URLSession = .shared, bundle: Bundle = .main
```

- parameter token: token of Management API v2 with the correct allowed scopes to perform the desired action
- parameter session: instance of NSURLSession used for networking. By default it will use the shared NSURLSession
- parameter session: instance of URLSession used for networking. By default it will use the shared URLSession
- parameter bundle: bundle used to locate the `Auth0.plist` file. By default is the main bundle

- returns: Auth0 Management API v2
Expand All @@ -117,7 +134,7 @@ public func users(token: String, session: URLSession = .shared, bundle: Bundle =

- parameter token: token of Management API v2 with the correct allowed scopes to perform the desired action
- parameter domain: domain of your Auth0 account. e.g.: 'samples.auth0.com'
- parameter session: instance of NSURLSession used for networking. By default it will use the shared NSURLSession
- parameter session: instance of URLSession used for networking. By default it will use the shared URLSession

- returns: Auth0 Management API v2
*/
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
43 changes: 25 additions & 18 deletions Auth0/Auth0WebAuth.swift
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
#if WEB_AUTH_PLATFORM
import AuthenticationServices
import Foundation

final class Auth0WebAuth: WebAuth {

let clientId: String
let url: URL
let session: URLSession
let storage: TransactionStore

var telemetry: Telemetry
var logger: Logger?
var ephemeralSession = false

#if os(macOS)
private let platform = "macos"
#else
private let platform = "ios"
#endif

private let responseType = "code"
private let requiredScope = "openid"

private(set) var parameters: [String: String] = [:]
private(set) var ephemeralSession = false
private(set) var issuer: String
private(set) var leeway: Int = 60 * 1000 // Default leeway is 60 seconds
private(set) var nonce: String?
private(set) var maxAge: Int?
private(set) var organization: String?
private(set) var invitationURL: URL?
private var nonce: String?
private var maxAge: Int?

lazy var redirectURL: URL? = {
guard let bundleIdentifier = Bundle.main.bundleIdentifier else { return nil }
Expand All @@ -38,10 +40,12 @@ final class Auth0WebAuth: WebAuth {

init(clientId: String,
url: URL,
session: URLSession = URLSession.shared,
storage: TransactionStore = TransactionStore.shared,
telemetry: Telemetry = Telemetry()) {
self.clientId = clientId
self.url = url
self.session = session
self.storage = storage
self.telemetry = telemetry
self.issuer = "\(url.absoluteString)/"
Expand Down Expand Up @@ -117,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 Expand Up @@ -207,18 +211,6 @@ final class Auth0WebAuth: WebAuth {
return components.url!
}

private func handler(_ redirectURL: URL) -> OAuth2Grant {
var authentication = Auth0Authentication(clientId: self.clientId, url: self.url, telemetry: self.telemetry)
authentication.logger = self.logger
return PKCE(authentication: authentication,
redirectURL: redirectURL,
issuer: self.issuer,
leeway: self.leeway,
maxAge: self.maxAge,
nonce: self.nonce,
organization: self.organization)
}

func generateDefaultState() -> String? {
let data = Data(count: 32)
var tempData = data
Expand All @@ -231,6 +223,21 @@ final class Auth0WebAuth: WebAuth {
return tempData.a0_encodeBase64URLSafe()
}

private func handler(_ redirectURL: URL) -> OAuth2Grant {
var authentication = Auth0Authentication(clientId: self.clientId,
url: self.url,
session: self.session,
telemetry: self.telemetry)
authentication.logger = self.logger
return PKCE(authentication: authentication,
redirectURL: redirectURL,
issuer: self.issuer,
leeway: self.leeway,
maxAge: self.maxAge,
nonce: self.nonce,
organization: self.organization)
}

}

extension Auth0Authentication {
Expand Down
4 changes: 2 additions & 2 deletions Auth0/AuthenticationError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,13 @@ extension AuthenticationError: CustomDebugStringConvertible {
extension AuthenticationError {

/**
Returns a value from the error's `info` dictionary
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? {
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
Loading

0 comments on commit aedd067

Please sign in to comment.