Skip to content

Strict swift concurrency support in the SDK #968

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

Draft
wants to merge 17 commits into
base: master
Choose a base branch
from
Draft
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
9 changes: 6 additions & 3 deletions App/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import UIKit
import Auth0

@UIApplicationMain
@main
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
return WebAuthentication.resume(with: url)
Task {
await WebAuthentication.resume(with: url)
}
return true
}

}
32 changes: 17 additions & 15 deletions App/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,25 @@ import Auth0
class ViewController: UIViewController {

@IBAction func login(_ sender: Any) {
Auth0
.webAuth()
.logging(enabled: true)
.start {
switch $0 {
case .failure(let error):
DispatchQueue.main.async {
self.alert(title: "Error", message: "\(error)")
}
case .success(let credentials):
DispatchQueue.main.async {
self.alert(title: "Success",
message: "Authorized and got a token \(credentials.accessToken)")
Task {
await Auth0
.webAuth()
.logging(enabled: true)
.start {
switch $0 {
case .failure(let error):
DispatchQueue.main.async {
self.alert(title: "Error", message: "\(error)")
}
case .success(let credentials):
DispatchQueue.main.async {
self.alert(title: "Success",
message: "Authorized and got a token \(credentials.accessToken)")
}
}
print($0)
}
print($0)
}
}
}

@IBAction func logout(_ sender: Any) {
Expand Down
3 changes: 2 additions & 1 deletion Auth0.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ Pod::Spec.new do |s|
s.social_media_url = 'https://twitter.com/auth0'
s.source_files = 'Auth0/**/*.swift'
s.resource_bundles = { s.name => 'Auth0/PrivacyInfo.xcprivacy' }
s.swift_versions = ['5.0']

s.dependency 'SimpleKeychain', '1.3.0'
s.dependency 'JWTDecode', '3.3.0'
Expand All @@ -26,6 +25,8 @@ Pod::Spec.new do |s|
s.osx.pod_target_xcconfig = {
'SWIFT_ACTIVE_COMPILATION_CONDITIONS' => 'WEB_AUTH_PLATFORM PASSKEYS_PLATFORM'
}

s.swift_versions = ['6.0', '6.1']

s.tvos.deployment_target = '14.0'
s.watchos.deployment_target = '7.0'
Expand Down
2 changes: 1 addition & 1 deletion Auth0/APICredentials.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ private struct _A0APICredentials {
}

/// User's credentials obtained from Auth0 for a specific API as the result of exchanging a refresh token.
public struct APICredentials: CustomStringConvertible {
public struct APICredentials: CustomStringConvertible, Sendable {

/// Token that can be used to make authenticated requests to the API.
///
Expand Down
84 changes: 47 additions & 37 deletions Auth0/ASProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,76 +8,86 @@ extension WebAuthentication {
static func asProvider(redirectURL: URL,
ephemeralSession: Bool = false,
headers: [String: String]? = nil) -> WebAuthProvider {
return { url, callback in
let session: ASWebAuthenticationSession

if #available(iOS 17.4, macOS 14.4, visionOS 1.2, *) {
if redirectURL.scheme == "https" {
session = ASWebAuthenticationSession(url: url,
callback: .https(host: redirectURL.host!,
path: redirectURL.path),
completionHandler: completionHandler(callback))
return { url, callback -> WebAuthUserAgent in

return await Task {
let session: ASWebAuthenticationSession

if #available(iOS 17.4, macOS 14.4, visionOS 1.2, *) {
if redirectURL.scheme == "https" {
session = ASWebAuthenticationSession(url: url,
callback: .https(host: redirectURL.host!,
path: redirectURL.path),
completionHandler: completionHandler(callback))
} else {
session = ASWebAuthenticationSession(url: url,
callback: .customScheme(redirectURL.scheme!),
completionHandler: completionHandler(callback))
}

session.additionalHeaderFields = headers
} else {
session = ASWebAuthenticationSession(url: url,
callback: .customScheme(redirectURL.scheme!),
callbackURLScheme: redirectURL.scheme,
completionHandler: completionHandler(callback))
}

session.additionalHeaderFields = headers
} else {
session = ASWebAuthenticationSession(url: url,
callbackURLScheme: redirectURL.scheme,
completionHandler: completionHandler(callback))
}

session.prefersEphemeralWebBrowserSession = ephemeralSession
session.prefersEphemeralWebBrowserSession = ephemeralSession

return ASUserAgent(session: session, callback: callback)
return await ASUserAgent(session: session, callback: callback)
}.value

}
}

static let completionHandler: (_ callback: @escaping WebAuthProviderCallback) -> ASHandler = { callback in
return {
guard let callbackURL = $0, $1 == nil else {
if let error = $1 as? NSError,
error.userInfo.isEmpty,
case ASWebAuthenticationSessionError.canceledLogin = error {
return callback(.failure(WebAuthError(code: .userCancelled)))
} else if let error = $1 {
return callback(.failure(WebAuthError(code: .other, cause: error)))
static let completionHandler: @Sendable (_ callback: @escaping WebAuthProviderCallback) -> ASHandler = { callback in
return { url,error in
Task {
guard let callbackURL = url, error == nil else {
if let error = error as? NSError,
error.userInfo.isEmpty,
case ASWebAuthenticationSessionError.canceledLogin = error {
return callback(.failure(WebAuthError(code: .userCancelled)))
} else if let error = error {
return callback(.failure(WebAuthError(code: .other, cause: error)))
}

return callback(.failure(WebAuthError(code: .unknown("ASWebAuthenticationSession failed"))))
}

return callback(.failure(WebAuthError(code: .unknown("ASWebAuthenticationSession failed"))))
_ = await TransactionStore.shared.resume(callbackURL)
}

_ = TransactionStore.shared.resume(callbackURL)
}
}
}

class ASUserAgent: NSObject, WebAuthUserAgent {
@MainActor final class ASUserAgent: NSObject, WebAuthUserAgent {

private(set) static var currentSession: ASWebAuthenticationSession?
let callback: WebAuthProviderCallback

init(session: ASWebAuthenticationSession, callback: @escaping WebAuthProviderCallback) {
init(session: ASWebAuthenticationSession, callback: @escaping @Sendable WebAuthProviderCallback) async {
self.callback = callback
super.init()

session.presentationContextProvider = self
await initializeSession(session: session)
}

func initializeSession(session: ASWebAuthenticationSession) async {
ASUserAgent.currentSession = session
}

func start() {
func start() async {
_ = ASUserAgent.currentSession?.start()
}

func finish(with result: WebAuthResult<Void>) {
func finish(with result: WebAuthResult<Void>) async {
ASUserAgent.currentSession?.cancel()
self.callback(result)
}

public override var description: String {
public nonisolated override var description: String {
return String(describing: ASWebAuthenticationSession.self)
}

Expand Down
Loading
Loading