Skip to content
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
1 change: 0 additions & 1 deletion Auth0/Auth0.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ public func authentication(session: URLSession = .shared, bundle: Bundle = .main
return authentication(clientId: values.clientId, domain: values.domain, session: session)
}


/**
Multi-Factor Authentication (MFA) client for performing MFA operations during authentication flows.

Expand Down
8 changes: 4 additions & 4 deletions Auth0/Auth0WebAuth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
private(set) var invitationURL: URL?
private(set) var overrideAuthorizeURL: URL?
private(set) var provider: WebAuthProvider?
private(set) var onCloseCallback: (() -> Void)?
private(set) var onCloseCallback: (@Sendable () -> Void)?
private(set) weak var presentationWindow: Auth0WindowRepresentable?

var state: String {
Expand Down Expand Up @@ -176,7 +176,7 @@
return self
}

func onClose(_ callback: (() -> Void)?) -> Self {
func onClose(_ callback: (@Sendable () -> Void)?) -> Self {
self.onCloseCallback = callback
return self
}
Expand All @@ -187,7 +187,7 @@
}

@MainActor
func start(_ callback: @escaping (WebAuthResult<Credentials>) -> Void) {
func start(_ callback: @escaping @Sendable (WebAuthResult<Credentials>) -> Void) {

Check warning on line 190 in Auth0/Auth0WebAuth.swift

View workflow job for this annotation

GitHub Actions / Test on iOS using Xcode 16.1

main actor-isolated instance method 'start' cannot be used to satisfy nonisolated protocol requirement; this is an error in the Swift 6 language mode

Check warning on line 190 in Auth0/Auth0WebAuth.swift

View workflow job for this annotation

GitHub Actions / Test on macOS using Xcode 16.1

main actor-isolated instance method 'start' cannot be used to satisfy nonisolated protocol requirement; this is an error in the Swift 6 language mode
let mainThreadCallback = dispatchOnMain(callback)
let mainThreadOnCloseCallback = onCloseCallback.map { dispatchOnMain($0) }

Expand Down Expand Up @@ -240,7 +240,7 @@
}

@MainActor
func logout(federated: Bool, callback: @escaping (WebAuthResult<Void>) -> Void) {
func logout(federated: Bool, callback: @escaping @Sendable (WebAuthResult<Void>) -> Void) {

Check warning on line 243 in Auth0/Auth0WebAuth.swift

View workflow job for this annotation

GitHub Actions / Test on iOS using Xcode 16.1

main actor-isolated instance method 'logout(federated:callback:)' cannot be used to satisfy nonisolated protocol requirement; this is an error in the Swift 6 language mode

Check warning on line 243 in Auth0/Auth0WebAuth.swift

View workflow job for this annotation

GitHub Actions / Test on macOS using Xcode 16.1

main actor-isolated instance method 'logout(federated:callback:)' cannot be used to satisfy nonisolated protocol requirement; this is an error in the Swift 6 language mode
let mainThreadCallback = dispatchOnMain(callback)

guard barrier.raise() else {
Expand Down Expand Up @@ -357,14 +357,14 @@

extension Auth0WebAuth {


Check warning on line 360 in Auth0/Auth0WebAuth.swift

View workflow job for this annotation

GitHub Actions / Lint code with SwiftLint

Limit vertical whitespace to a single empty line; currently 2 (vertical_whitespace)

Check warning on line 360 in Auth0/Auth0WebAuth.swift

View workflow job for this annotation

GitHub Actions / Lint code with SwiftLint

Lines should not have trailing whitespace (trailing_whitespace)
@MainActor
public func start() -> AnyPublisher<Credentials, WebAuthError> {

Check warning on line 362 in Auth0/Auth0WebAuth.swift

View workflow job for this annotation

GitHub Actions / Test on iOS using Xcode 16.1

main actor-isolated instance method 'start()' cannot be used to satisfy nonisolated protocol requirement; this is an error in the Swift 6 language mode

Check warning on line 362 in Auth0/Auth0WebAuth.swift

View workflow job for this annotation

GitHub Actions / Test on macOS using Xcode 16.1

main actor-isolated instance method 'start()' cannot be used to satisfy nonisolated protocol requirement; this is an error in the Swift 6 language mode
return Deferred { Future(self.start) }.eraseToAnyPublisher()
}

Check warning on line 365 in Auth0/Auth0WebAuth.swift

View workflow job for this annotation

GitHub Actions / Lint code with SwiftLint

Lines should not have trailing whitespace (trailing_whitespace)
@MainActor
public func logout(federated: Bool) -> AnyPublisher<Void, WebAuthError> {

Check warning on line 367 in Auth0/Auth0WebAuth.swift

View workflow job for this annotation

GitHub Actions / Test on iOS using Xcode 16.1

main actor-isolated instance method 'logout(federated:)' cannot be used to satisfy nonisolated protocol requirement; this is an error in the Swift 6 language mode

Check warning on line 367 in Auth0/Auth0WebAuth.swift

View workflow job for this annotation

GitHub Actions / Test on macOS using Xcode 16.1

main actor-isolated instance method 'logout(federated:)' cannot be used to satisfy nonisolated protocol requirement; this is an error in the Swift 6 language mode
return Deferred {
Future { callback in
self.logout(federated: federated) { result in
Expand Down Expand Up @@ -406,4 +406,4 @@

}
#endif
#endif

Check warning on line 409 in Auth0/Auth0WebAuth.swift

View workflow job for this annotation

GitHub Actions / Lint code with SwiftLint

File should contain 400 lines or less: currently contains 409 (file_length)
20 changes: 10 additions & 10 deletions Auth0/CredentialsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@
/// - [Refresh Tokens](https://auth0.com/docs/secure/tokens/refresh-tokens)
/// - [Authentication API Endpoint](https://auth0.com/docs/api/authentication/revoke-refresh-token/revoke-refresh-token)
public func revoke(headers: [String: String] = [:],
_ callback: @escaping (CredentialsManagerResult<Void>) -> Void) {
_ callback: @escaping @Sendable (CredentialsManagerResult<Void>) -> Void) {
let mainThreadCallback = dispatchOnMain(callback)

guard let credentials = self.retrieveCredentials(),
Expand Down Expand Up @@ -417,7 +417,7 @@
minTTL: Int = 60,
parameters: [String: Any] = [:],
headers: [String: String] = [:],
callback: @escaping (CredentialsManagerResult<Credentials>) -> Void) {
callback: @escaping @Sendable (CredentialsManagerResult<Credentials>) -> Void) {
let mainThreadCallback = dispatchOnMain(callback)

if let bioAuth = self.bioAuth {
Expand Down Expand Up @@ -526,7 +526,7 @@
minTTL: Int = 60,
parameters: [String: Any] = [:],
headers: [String: String] = [:],
callback: @escaping (CredentialsManagerResult<Credentials>) -> Void) {
callback: @escaping @Sendable (CredentialsManagerResult<Credentials>) -> Void) {
self.retrieveCredentials(scope: scope,
minTTL: minTTL,
parameters: parameters,
Expand Down Expand Up @@ -604,7 +604,7 @@
minTTL: Int = 60,
parameters: [String: Any] = [:],
headers: [String: String] = [:],
callback: @escaping (CredentialsManagerResult<APICredentials>) -> Void) {
callback: @escaping @Sendable (CredentialsManagerResult<APICredentials>) -> Void) {
self.retrieveAPICredentials(audience: audience,
scope: scope,
minTTL: minTTL,
Expand Down Expand Up @@ -678,7 +678,7 @@
/// - <doc:RefreshTokens>
public func ssoCredentials(parameters: [String: Any] = [:],
headers: [String: String] = [:],
callback: @escaping (CredentialsManagerResult<SSOCredentials>) -> Void) {
callback: @escaping @Sendable (CredentialsManagerResult<SSOCredentials>) -> Void) {
self.retrieveSSOCredentials(parameters: parameters, headers: headers, callback: dispatchOnMain(callback))
}

Expand Down Expand Up @@ -723,7 +723,7 @@
/// - <doc:RefreshTokens>
public func renew(parameters: [String: Any] = [:],
headers: [String: String] = [:],
callback: @escaping (CredentialsManagerResult<Credentials>) -> Void) {
callback: @escaping @Sendable (CredentialsManagerResult<Credentials>) -> Void) {
self.retrieveCredentials(scope: nil,
minTTL: 0,
parameters: parameters,
Expand Down Expand Up @@ -771,7 +771,7 @@
parameters: [String: Any],
headers: [String: String],
forceRenewal: Bool,
callback: @escaping (CredentialsManagerResult<Credentials>) -> Void) {
callback: @escaping @Sendable (CredentialsManagerResult<Credentials>) -> Void) {
self.retrieveCredentialsWithRetry(scope: scope,
minTTL: minTTL,
parameters: parameters,
Expand All @@ -788,7 +788,7 @@
headers: [String: String],
forceRenewal: Bool,
retryCount: Int,
callback: @escaping (CredentialsManagerResult<Credentials>) -> Void) {
callback: @escaping @Sendable (CredentialsManagerResult<Credentials>) -> Void) {
SynchronizationBarrier.shared.execute { complete in
guard let credentials = self.retrieveCredentials() else {
complete()
Expand Down Expand Up @@ -857,7 +857,7 @@

private func retrieveSSOCredentials(parameters: [String: Any],
headers: [String: String],
callback: @escaping (CredentialsManagerResult<SSOCredentials>) -> Void) {
callback: @escaping @Sendable (CredentialsManagerResult<SSOCredentials>) -> Void) {
SynchronizationBarrier.shared.execute { complete in
guard let credentials = self.retrieveCredentials() else {
complete()
Expand Down Expand Up @@ -899,7 +899,7 @@
minTTL: Int,
parameters: [String: Any],
headers: [String: String],
callback: @escaping (CredentialsManagerResult<APICredentials>) -> Void) {
callback: @escaping @Sendable (CredentialsManagerResult<APICredentials>) -> Void) {
SynchronizationBarrier.shared.execute { complete in
if let apiCredentials = self.retrieveAPICredentials(audience: audience, scope: scope),
!self.hasExpired(apiCredentials.expiresAt),
Expand Down Expand Up @@ -1034,7 +1034,7 @@
func revoke(headers: [String: String] = [:]) -> AnyPublisher<Void, CredentialsManagerError> {
return Deferred {
Future { callback in
return self.revoke(headers: headers, callback)

Check warning on line 1037 in Auth0/CredentialsManager.swift

View workflow job for this annotation

GitHub Actions / Test on macOS using Xcode 16.1

passing non-sendable parameter 'callback' to function expecting a @sendable closure

Check warning on line 1037 in Auth0/CredentialsManager.swift

View workflow job for this annotation

GitHub Actions / Test on tvOS using Xcode 16.1

passing non-sendable parameter 'callback' to function expecting a @sendable closure
}
}.eraseToAnyPublisher()
}
Expand Down Expand Up @@ -1121,7 +1121,7 @@
minTTL: minTTL,
parameters: parameters,
headers: headers,
callback: callback)

Check warning on line 1124 in Auth0/CredentialsManager.swift

View workflow job for this annotation

GitHub Actions / Test on macOS using Xcode 16.1

passing non-sendable parameter 'callback' to function expecting a @sendable closure

Check warning on line 1124 in Auth0/CredentialsManager.swift

View workflow job for this annotation

GitHub Actions / Test on tvOS using Xcode 16.1

passing non-sendable parameter 'callback' to function expecting a @sendable closure
}
}.eraseToAnyPublisher()
}
Expand Down Expand Up @@ -1214,7 +1214,7 @@
minTTL: minTTL,
parameters: parameters,
headers: headers,
callback: callback)

Check warning on line 1217 in Auth0/CredentialsManager.swift

View workflow job for this annotation

GitHub Actions / Test on macOS using Xcode 16.1

passing non-sendable parameter 'callback' to function expecting a @sendable closure

Check warning on line 1217 in Auth0/CredentialsManager.swift

View workflow job for this annotation

GitHub Actions / Test on tvOS using Xcode 16.1

passing non-sendable parameter 'callback' to function expecting a @sendable closure
}
}.eraseToAnyPublisher()
}
Expand Down Expand Up @@ -1291,7 +1291,7 @@
headers: [String: String] = [:]) -> AnyPublisher<SSOCredentials, CredentialsManagerError> {
return Deferred {
Future { callback in
return self.ssoCredentials(parameters: parameters, headers: headers, callback: callback)

Check warning on line 1294 in Auth0/CredentialsManager.swift

View workflow job for this annotation

GitHub Actions / Test on macOS using Xcode 16.1

passing non-sendable parameter 'callback' to function expecting a @sendable closure

Check warning on line 1294 in Auth0/CredentialsManager.swift

View workflow job for this annotation

GitHub Actions / Test on tvOS using Xcode 16.1

passing non-sendable parameter 'callback' to function expecting a @sendable closure
}
}.eraseToAnyPublisher()
}
Expand Down Expand Up @@ -1347,7 +1347,7 @@
Future { callback in
return self.renew(parameters: parameters,
headers: headers,
callback: callback)

Check warning on line 1350 in Auth0/CredentialsManager.swift

View workflow job for this annotation

GitHub Actions / Test on macOS using Xcode 16.1

passing non-sendable parameter 'callback' to function expecting a @sendable closure

Check warning on line 1350 in Auth0/CredentialsManager.swift

View workflow job for this annotation

GitHub Actions / Test on tvOS using Xcode 16.1

passing non-sendable parameter 'callback' to function expecting a @sendable closure
}
}.eraseToAnyPublisher()
}
Expand Down
2 changes: 1 addition & 1 deletion Auth0/IDTokenSignatureValidator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ struct IDTokenSignatureValidator: JWTAsyncValidator {
self.context = context
}

func validate(_ jwt: JWT, callback: @escaping (Auth0Error?) -> Void) {
func validate(_ jwt: JWT, callback: @escaping @Sendable (Auth0Error?) -> Void) {
if let error = validateAlg(jwt) { return callback(error) }
let algorithm = jwt.algorithm!
if let error = validateKid(jwt) { return callback(error) }
Expand Down
6 changes: 3 additions & 3 deletions Auth0/IDTokenValidator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ protocol JWTValidator: Sendable {
}

protocol JWTAsyncValidator: Sendable {
func validate(_ jwt: JWT, callback: @escaping (Auth0Error?) -> Void)
func validate(_ jwt: JWT, callback: @escaping @Sendable (Auth0Error?) -> Void)
}

struct IDTokenValidator: JWTAsyncValidator {
Expand All @@ -34,7 +34,7 @@ struct IDTokenValidator: JWTAsyncValidator {
self.context = context
}

func validate(_ jwt: JWT, callback: @escaping (Auth0Error?) -> Void) {
func validate(_ jwt: JWT, callback: @escaping @Sendable (Auth0Error?) -> Void) {
DispatchQueue.global(qos: .userInitiated).async {
self.signatureValidator.validate(jwt) { error in
if let error = error { return callback(error) }
Expand Down Expand Up @@ -63,7 +63,7 @@ func validate(idToken: String,
with context: IDTokenValidatorContext,
signatureValidator: JWTAsyncValidator? = nil, // for testing
claimsValidator: JWTValidator? = nil,
callback: @escaping (Auth0Error?) -> Void) {
callback: @escaping @Sendable (Auth0Error?) -> Void) {
guard let jwt = try? decode(jwt: idToken) else { return callback(IDTokenDecodingError.cannotDecode) }
var claimValidators: [JWTValidator] = [IDTokenIssValidator(issuer: context.issuer),
IDTokenSubValidator(),
Expand Down
2 changes: 1 addition & 1 deletion Auth0/LoginTransaction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Foundation

class LoginTransaction: NSObject, AuthTransaction {

typealias FinishTransaction = (WebAuthResult<Credentials>) -> Void
typealias FinishTransaction = @Sendable (WebAuthResult<Credentials>) -> Void

private(set) var userAgent: WebAuthUserAgent?

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

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

Expand Down Expand Up @@ -63,7 +63,7 @@ struct PKCE: OAuth2Grant {
self.defaults = newDefaults
}

func credentials(from values: [String: String], callback: @escaping (WebAuthResult<Credentials>) -> Void) {
func credentials(from values: [String: String], callback: @escaping @Sendable (WebAuthResult<Credentials>) -> Void) {
guard let code = values["code"] else {
return callback(.failure(WebAuthError(code: .unknown("Authorization code missing from callback URL query parameters (\(values))"))))
}
Expand Down
6 changes: 3 additions & 3 deletions Auth0/Request.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ public struct Request<T, E: Auth0APIError>: Requestable, @unchecked Sendable {
/**
The callback closure type for the request.
*/
public typealias Callback = (Result<T, E>) -> Void
public typealias Callback = @Sendable (Result<T, E>) -> Void

let session: URLSession
let url: URL
let method: String
let requestValidator: [RequestValidator]
let handle: (Result<ResponseValue, E>, Callback) -> Void
let handle: @Sendable (Result<ResponseValue, E>, Callback) -> Void
let parameters: [String: Any]
let headers: [String: String]
let logger: Logger?
Expand All @@ -39,7 +39,7 @@ public struct Request<T, E: Auth0APIError>: Requestable, @unchecked Sendable {
url: URL,
method: String,
requestValidator: [RequestValidator] = [],
handle: @escaping (Result<ResponseValue, E>, Callback) -> Void,
handle: @escaping @Sendable (Result<ResponseValue, E>, Callback) -> Void,
parameters: [String: Any] = [:],
headers: [String: String] = [:],
logger: Logger?,
Expand Down
2 changes: 1 addition & 1 deletion Auth0/Requestable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
func parameters(_ extraParameters: [String: Any]) -> any Requestable<ResultType, ErrorType>
func headers(_ extraHeaders: [String: String]) -> any Requestable<ResultType, ErrorType>
func requestValidators(_ extraValidators: [RequestValidator]) -> any Requestable<ResultType, ErrorType>
func start(_ callback: @escaping (Result<ResultType, ErrorType>) -> Void)
func start(_ callback: @escaping @Sendable (Result<ResultType, ErrorType>) -> Void)
func start() -> AnyPublisher<ResultType, ErrorType>
func start() async throws -> ResultType
}
Expand All @@ -16,17 +16,17 @@
func parameters(_ extraParameters: [String: Any]) -> any Requestable<ResultType, ErrorType> {
self
}

Check warning on line 19 in Auth0/Requestable.swift

View workflow job for this annotation

GitHub Actions / Lint code with SwiftLint

Lines should not have trailing whitespace (trailing_whitespace)
func headers(_ extraHeaders: [String: String]) -> any Requestable<ResultType, ErrorType> {
self
}

Check warning on line 23 in Auth0/Requestable.swift

View workflow job for this annotation

GitHub Actions / Lint code with SwiftLint

Lines should not have trailing whitespace (trailing_whitespace)
func requestValidators(_ extraValidators: [RequestValidator]) -> any Requestable<ResultType, ErrorType> {
self
}
}


Check warning on line 29 in Auth0/Requestable.swift

View workflow job for this annotation

GitHub Actions / Lint code with SwiftLint

Limit vertical whitespace to a single empty line; currently 2 (vertical_whitespace)
// MARK: - Combine

public extension Requestable {
Expand All @@ -37,7 +37,7 @@
- Returns: A type-erased publisher.
*/
func start() -> AnyPublisher<ResultType, ErrorType> {
return Deferred { Future(self.start) }.eraseToAnyPublisher()

Check warning on line 40 in Auth0/Requestable.swift

View workflow job for this annotation

GitHub Actions / Test on tvOS using Xcode 16.1

converting non-sendable function value to '@sendable (Result<Self.ResultType, Self.ErrorType>) -> Void' may introduce data races
}

}
Expand Down
4 changes: 2 additions & 2 deletions Auth0/Shared.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ struct SendableBox<T>: @unchecked Sendable {

/// Ensures a callback is executed on the main thread.
/// If already on the main thread, executes immediately. Otherwise, dispatches asynchronously to main.
func dispatchOnMain<T>(_ callback: @escaping (T) -> Void) -> (T) -> Void {
func dispatchOnMain<T>(_ callback: @escaping @Sendable (T) -> Void) -> @Sendable (T) -> Void {
return { result in
if Thread.isMainThread {
callback(result)
Expand All @@ -51,7 +51,7 @@ func dispatchOnMain<T>(_ callback: @escaping (T) -> Void) -> (T) -> Void {
}
}

func dispatchOnMain(_ callback: @escaping () -> Void) -> () -> Void {
func dispatchOnMain(_ callback: @escaping @Sendable () -> Void) -> @Sendable () -> Void {
let wrapped = dispatchOnMain { (_: Void) in callback() }
return { wrapped(()) }
}
7 changes: 4 additions & 3 deletions Auth0/SynchronizationBarrier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import Foundation
/// The barrier uses a serial queue to maintain operation ordering and a queue-based system
/// to ensure each operation completes before the next one begins, without using dispatch groups
/// or semaphores that would block threads.
final class SynchronizationBarrier {
/// @unchecked is safe here: as we are locking the access using serial queues
final class SynchronizationBarrier: @unchecked Sendable {

/// Shared singleton instance used by both CredentialsManager and Auth0Authentication
static let shared = SynchronizationBarrier()
Expand All @@ -16,7 +17,7 @@ final class SynchronizationBarrier {
private let queue = DispatchQueue(label: "com.auth0.synchronization.serial")

/// Queue of pending operations waiting to be executed
private var pendingOperations: [(@escaping () -> Void) -> Void] = []
private var pendingOperations: [@Sendable (@escaping @Sendable () -> Void) -> Void] = []

/// Flag indicating whether an operation is currently executing
private var isExecuting = false
Expand All @@ -33,7 +34,7 @@ final class SynchronizationBarrier {
/// - Parameter operation: The operation to execute. The operation will receive a completion
/// handler that it **must** call when finished. The completion handler can be called from
/// any thread. Operations are executed one at a time in the order they were submitted.
func execute(_ operation: @escaping (@escaping () -> Void) -> Void) {
func execute(_ operation: @escaping @Sendable (@escaping @Sendable () -> Void) -> Void) {

queue.async { [weak self] in
guard let self = self else { return }
Expand Down
8 changes: 4 additions & 4 deletions Auth0/WebAuth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ public protocol WebAuth: SenderConstraining, Trackable, Loggable {
///
/// - Parameter callback: A callback to be executed
/// - Returns: The same `WebAuth` instance to allow method chaining.
func onClose(_ callback: (() -> Void)?) -> Self
func onClose(_ callback: (@Sendable () -> Void)?) -> Self

/// Specify a custom UIWindow/NSWindow to present the in-app browser.
/// If not specified, the system will use the active key window.
Expand Down Expand Up @@ -266,7 +266,7 @@ public protocol WebAuth: SenderConstraining, Trackable, Loggable {
- Requires: The **Callback URL** to have been added to the **Allowed Callback URLs** field of your Auth0
application settings in the [Dashboard](https://manage.auth0.com/#/applications/).
*/
func start(_ callback: @escaping (WebAuthResult<Credentials>) -> Void)
func start(_ callback: @escaping @Sendable (WebAuthResult<Credentials>) -> Void)

#if canImport(_Concurrency)
/**
Expand Down Expand Up @@ -359,7 +359,7 @@ public protocol WebAuth: SenderConstraining, Trackable, Loggable {

- [Logout](https://auth0.com/docs/authenticate/login/logout)
*/
func logout(federated: Bool, callback: @escaping (WebAuthResult<Void>) -> Void)
func logout(federated: Bool, callback: @escaping @Sendable (WebAuthResult<Void>) -> Void)

/**
Removes the Auth0 session and optionally removes the identity provider (IdP) session.
Expand Down Expand Up @@ -443,7 +443,7 @@ public protocol WebAuth: SenderConstraining, Trackable, Loggable {

public extension WebAuth {

func logout(federated: Bool = false, callback: @escaping (WebAuthResult<Void>) -> Void) {
func logout(federated: Bool = false, callback: @escaping @Sendable (WebAuthResult<Void>) -> Void) {
self.logout(federated: federated, callback: callback)
}

Expand Down
39 changes: 38 additions & 1 deletion V3_MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ As expected with a major release, Auth0.swift v3 contains breaking changes. Plea
- [**Swift 6 Concurrency**](#swift-6-concurrency)
+ [Sendable protocol conformances](#sendable-protocol-conformances)
+ [Sendable conformances for Web Auth typealiases](#sendable-conformances-for-web-auth-typealiases)
+ [@Sendable callback parameters](#sendable-callback-parameters)
- [**API Changes**](#api-changes)
+ [WebAuthError cases](#webautherror-cases)
+ [Renamed APIs](#renamed-apis)
Expand Down Expand Up @@ -371,6 +372,42 @@ final class MyLogger: Logger, @unchecked Sendable {

**Impact:** If you pass a closure as a `WebAuthProviderCallback`, all values it captures must be `Sendable`. In most cases this is automatically satisfied, but if your closure captures a non-`Sendable` class you will get a compiler warning under strict concurrency.

### @Sendable callback parameters

**Change:** All public callback parameters are now `@Sendable`. This affects the following APIs:

- `Requestable.start(_:)`
- `WebAuth.start(_:)`, `WebAuth.logout(federated:callback:)`, `WebAuth.onClose(_:)`
- `CredentialsManager.credentials(withScope:minTTL:parameters:headers:callback:)`, `revoke(headers:_:)`, `apiCredentials(forAudience:scope:minTTL:parameters:headers:callback:)`, `ssoCredentials(parameters:headers:callback:)`, `renew(parameters:headers:callback:)`

**Impact:**

- **Call sites** — No changes required. The compiler infers `@Sendable` automatically for typical trailing closures. You will only see a compiler error in Swift 6 mode if your closure captures a non-`Sendable` type.

- **Custom protocol implementations** (mocks, test doubles) — If you implement `Requestable`, `TokenRequestable`, `WebAuth`, or any other protocol whose method signatures include a callback, you must add `@Sendable` to the matching parameter.

<details>
<summary>Migration example</summary>

```swift
// v2 - start without @Sendable
struct MockRequestable: Requestable {
func start(_ callback: @escaping (Result<Credentials, AuthenticationError>) -> Void) {
callback(.success(mockCredentials))
}
// ...
}

// v3 - add @Sendable to match the updated protocol requirement
struct MockRequestable: Requestable {
func start(_ callback: @escaping @Sendable (Result<Credentials, AuthenticationError>) -> Void) {
callback(.success(mockCredentials))
}
// ...
}
```
</details>

---

## API Changes
Expand Down Expand Up @@ -506,7 +543,7 @@ struct MockTokenRequest: TokenRequestable {

let mockResult: Result<Credentials, AuthenticationError>

func start(_ callback: @escaping (Result<Credentials, AuthenticationError>) -> Void) {
func start(_ callback: @escaping @Sendable (Result<Credentials, AuthenticationError>) -> Void) {
callback(mockResult)
}

Expand Down
Loading