Auth0.swift v2 includes many significant changes:
- Thread-safe renewal of credentials from the Credentials Manager.
- Support for custom storage layer in the Credentials Manager.
- Support for async/await and Combine.
- Support for custom headers.
- Adoption of the Swift 5
Result
type. - Simplified error handling.
As expected with a major release, Auth0.swift v2 contains breaking changes. Please review this guide thorougly to understand the changes required to migrate your application to v2.
- Supported Languages
- Supported Platform Versions
- Default Values
- Types Removed
- Methods Removed
- Types Changed
- Type Properties Changed
- Method Signatures Changed
- Behavior Changes
The minimum supported Swift version is now 5.3.
Auth0.swift no longer supports Objective-C.
The deployment targets for each platform were raised to:
- iOS 12.0
- macOS 10.15
- Mac Catalyst 13.0
- tvOS 12.0
- watchOS 6.2
The default scope value in Web Auth and all the Authentication client methods (except renew(withRefreshToken:scope:)
, in which scope
keeps defaulting to nil
) was changed from an assortment of values to openid profile email
.
The following protocols were removed:
AuthResumable
AuthCancelable
AuthProvider
NativeAuthTransaction
AuthResumable
and AuthCancelable
were subsumed in AuthTransaction
, which is no longer public.
The iOS-only type alias A0URLOptionsKey
was removed, as it is no longer needed.
- The custom
Result
enum was removed, along with its shims. Auth0.swift is now using the Swift 5Result
type. - The
Challenge.CodingKeys
enum is no longer public.
The following structs were removed, as they were no longer being used:
NativeAuthCredentials
ConcatRequest
The following Objective-C compatibility wrappers were removed:
_ObjectiveAuthenticationAPI
_ObjectiveManagementAPI
_ObjectiveOAuth2
The following classes were also removed, as they were no longer being used:
Profile
Identity
You should use UserInfo
from userInfo(withAccessToken:)
instead.
The iOS-only method resumeAuth(_:options:)
and the macOS-only method resumeAuth(_:)
were removed from the library, as they are no longer needed. You can safely remove them from your application.
You should use login(usernameOrEmail:password:realmOrConnection:audience:scope:)
instead.
Before / After
// Before
Auth0
.authentication()
.login(usernameOrEmail: username,
password: password,
connection: connection,
scope: scope,
parameters: ["key": "value"])
.start { result in
// ...
}
// After
Auth0
.authentication()
.login(usernameOrEmail: username,
password: password,
realmOrConnection: connection,
scope: scope)
.parameters(["key": "value"])
.start { result in
// ...
}
}
For multi-factor authentication, use multifactorChallenge(mfaToken:types:authenticatorId:)
and then either login(withOTP:mfaToken:)
or login(withOOBCode:mfaToken:bindingCode:)
.
You should use signup(email:username:password:connection:userMetadata:rootAttributes:)
and then login(usernameOrEmail:password:realmOrConnection:audience:scope:)
instead. That is, first create the user and then log them in.
Before / After
// Before
Auth0
.authentication()
.signUp(email: email,
username: username,
password: password,
connection: connection,
userMetadata: metadata,
scope: scope,
parameters: ["key": "value"])
.start { result in
// ...
}
// After
Auth0
.authentication()
.signup(email: email,
username: username,
password: password,
connection: connection,
userMetadata: metadata)
.start { result in
switch result {
case .success:
Auth0
.authentication()
.login(usernameOrEmail: username,
password: password,
realmOrConnection: connection,
scope: scope)
.parameters(["key": "value"])
.start { result in
// ...
}
case .failure(let error): // ...
}
}
}
You should use codeExchange(withCode:codeVerifier:redirectURI:)
instead.
Before / After
// Before
Auth0
.authentication()
.tokenExchange(withParameters: ["key": "value"])
.start { result in
// ...
}
// After
Auth0
.authentication()
.codeExchange(withCode: code,
codeVerifier: codeVerifier,
redirectURI: redirectURI)
.parameters(["key": "value"])
.start { result in
// ...
}
}
You should use login(appleAuthorizationCode:fullName:profile:audience:scope:)
instead.
You should use userInfo(withAccessToken:)
instead.
You should use Web Auth with its connection(_:)
method instead.
Before / After
// Before
Auth0
.authentication()
.webAuth(withConnection: connection)
.start { result in
// ...
}
}
// After
Auth0
.webAuth()
.connection(connection)
.start { result in
// ...
}
}
The method relies on a deprecated endpoint and has no direct replacement. You can use login(appleAuthorizationCode:fullName:profile:audience:scope:)
and/or login(facebookSessionAccessToken:profile:audience:scope:)
instead.
The method relies on a deprecated endpoint and has no replacement.
Auth0.swift now only supports the authorization code flow with PKCE, which is used by default. For this reason, the following methods were removed from the Web Auth builder:
usingImplicitGrant()
responseType(_:)
The useUniversalLink()
method was removed as well, as Universal Links cannot be used for OAuth redirections without user interaction since iOS 10.
Important
Check the FAQ for more information about the alert box that pops up by default when using Web Auth.
useLegacyAuthentication()
and useLegacyAuthentication(withStyle:)
were removed. You should use WebAuthentication.safariProvider()
instead (requires Auth0.swift 2.10+).
Before / After
// Before
Auth0
.webAuth()
.useLegacyAuthentication()
.start { result in
// ...
}
}
// After
Auth0
.webAuth()
.provider(WebAuthentication.safariProvider())
.start { result in
// ...
}
}
The method enableTouchAuth(withTitle:cancelTitle:fallbackTitle:)
was removed. You should use enableBiometrics(withTitle:cancelTitle:fallbackTitle:evaluationPolicy:)
instead.
The init(string: String?, statusCode: Int)
initializer was removed.
The init(string: String?, statusCode: Int)
initializer was removed.
The init(string: String?, statusCode: Int)
initializer was removed.
The a0_url(_:)
method is no longer public.
Auth0Error
was renamed toAuth0APIError
, andAuth0Error
is now a different protocol.Credentials
is now afinal
class that conforms toCodable
instead ofJSONObjectPayload
.UserPatchAttributes
is now afinal
class.UserInfo
was changed from class to struct.AuthenticationError
was changed from class to struct, and it no longer conforms toCustomNSError
.ManagementError
was changed from class to struct, and it no longer conforms toCustomNSError
.WebAuthError
was changed from enum to struct.CredentialsManagerError
was changed from enum to struct.
The following cases were lowercased, as per the naming convention of Swift 3+:
.Code
->.code
.WebLink
->.webLink
.AndroidLink
->.androidLink
The property description
was removed in favor of localizedDescription
, as AuthenticationError
now conforms to LocalizedError
.
The property description
was removed in favor of localizedDescription
, as ManagementError
now conforms to LocalizedError
.
All the former enum cases are now static properties, so to switch over them you will need to add a default
clause.
Before / After
// Before
switch error {
case .userCancelled: // handle WebAuthError
// ...
}
// After
switch error {
case .userCancelled: // handle WebAuthError
// ...
default: // handle unknown errors, for example errors added in future versions
}
infoKey
errorDomain
errorCode
errorUserInfo
All the following error cases were no longer being used.
.noNonceProvided
.invalidIdTokenNonce
.cannotDismissWebAuthController
.missingResponseParam
.missingAccessToken
.unknownError
was renamed to.unknown
..noBundleIdentifierFound
was renamed to.noBundleIdentifier
.
.invalidInvitationURL
, for when the invitation URL is missing theorganization
and/or theinvitation
query parameters..noAuthorizationCode
, for when the callback URL is missing thecode
query parameter..idTokenValidationFailed
, for when the ID token validation performed after Web Auth login fails..other
, for when a differentError
happens. That error can be accessed via thecause: Error?
property.
All the former enum cases are now static properties, so to switch over them you will need to add a default
clause.
As static properties cannot have associated values, to access the underlying Error
for .renewFailed
, .biometricsFailed
, and .revokeFailed
use the new cause: Error?
property.
Before / After
// Before
switch error {
case .revokeFailed(let error): handleError(error) // handle underlying Error
// ...
}
// After
switch error {
case .revokeFailed: handleError(error.cause) // handle underlying Error?
// ...
default: // handle unknown errors, for example errors added in future versions
}
.failedRefresh
was renamed to.renewFailed
..touchFailed
was renamed to.biometricsFailed
.
.largeMinTTL
, for when the requested minTTL
is greater than the lifetime of the renewed access token.
It is now a struct, so its properties are no longer marked with the @objc
attribute.
The properties are no longer marked with the @objc
attribute. Additionally, the following properties are no longer optional:
accessToken
tokenType
expiresIn
idToken
These properties were removed:
a0_isManagementError
a0_isAuthenticationError
The methods of the Authentication API client now only yield errors of type AuthenticationError
. The underlying error value (if any) is available via the cause: Error?
property of the AuthenticationError
value.
Before / After
// Before
switch error {
case .success(let credentials): // ...
case .failure(let error as AuthenticationError): // handle AuthenticationError
case .failure(let error): // handle Error
}
// After
switch error {
case .success(let credentials): // ...
case .failure(let error): // handle AuthenticationError
}
The method createUser(email:username:password:connection:userMetadata:rootAttributes:)
was renamed to signup(email:username:password:connection:userMetadata:rootAttributes:)
.
The method tokenExchange(withCode:codeVerifier:redirectURI:)
was renamed to codeExchange(withCode:codeVerifier:redirectURI:)
.
The following methods lost the parameters
parameter:
login(phoneNumber:code:audience:scope:)
login(usernameOrEmail:password:realmOrConnection:audience:scope:)
loginDefaultDirectory(withUsername:password:audience:scope:)
startPasswordless(email:type:connection:)
To pass custom parameters to those (or any) method in the Authentication client, use the parameters(_:)
method from Request
:
Auth0
.authentication()
.renew(withRefreshToken: credentials.refreshToken) // Returns a Request
.parameters(["key": "value"]) // 👈🏻
.start { result in
// ...
}
In the method login(usernameOrEmail:password:realmOrConnection:audience:scope:)
the realm
parameter was renamed to realmOrConnection
.
In the following methods the scope
and audience
parameters switched places, for consistency with the rest of the methods in the Authentication client:
login(appleAuthorizationCode:fullName:profile:audience:scope:)
login(facebookSessionAccessToken:profile:audience:scope:)
In the following methods the scope
parameter became non-optional, with a default value of openid profile email
:
login(email:code:audience:scope:)
login(phoneNumber:code:audience:scope:)
login(usernameOrEmail:password:realmOrConnection:audience:scope:)
loginDefaultDirectory(withUsername:password:audience:scope:)
login(appleAuthorizationCode:fullName:profile:audience:scope:)
login(facebookSessionAccessToken:profile:audience:scope:)
The multifactorChallenge(mfaToken:types:authenticatorId:)
method lost its channel
parameter, which is no longer necessary.
The methods of the Management API client now only yield errors of type ManagementError
. The underlying error value (if any) is available via the cause: Error?
property of the ManagementError
value.
Before / After
// Before
switch error {
case .success(let user): // ...
case .failure(let error as ManagementError): // handle ManagementError
case .failure(let error): // handle Error
}
// After
switch error {
case .success(let user): // ...
case .failure(let error): // handle ManagementError
}
The Web Auth methods now only yield errors of type WebAuthError
. The underlying error value (if any) is available via the cause: Error?
property of the WebAuthError
value.
Before / After
// Before
switch result {
case .success(let credentials): // ...
case .failure(let error as WebAuthError): // handle WebAuthError
case .failure(let error): // handle Error
}
// After
switch result {
case .success(let credentials): // ...
case .failure(let error): // handle WebAuthError
}
This method now yields a Result<Void, WebAuthError>
, which is aliased to WebAuthResult<Void>
. This means you can now check the type of error, for example if the user cancelled the operation.
Before / After
// Before
Auth0
.webAuth()
.clearSession(federated: false) { outcome in
switch outcome {
case true: // success
case false: // failure
}
}
// After
Auth0
.webAuth()
.clearSession() { result in // federated is now false by default
switch result {
case .success: // ...
case .failure(let error): // ...
}
}
The methods of the Credentials Manager now only yield errors of type CredentialsManagerError
. The underlying error value (if any) is available via the cause: Error?
property of the CredentialsManagerError
value.
Before / After
// Before
if let error = error as? CredentialsManagerError {
// handle CredentialsManagerError
}
// After
switch result {
case .success(let credentials): // ...
case .failure(let error): // handle CredentialsManagerError
}
CredentialsManager
now takes a CredentialsStorage
protocol as its storage argument rather than an instance of SimpleKeychain
.
This means you can now provide your own storage layer to CredentialsManager
.
class CustomStore: CredentialsStorage {
var store: [String : Data] = [:]
func getEntry(forKey key: String) -> Data? {
return store[key]
}
func setEntry(_ data: Data, forKey key: String) -> Bool {
store[key] = data
return true
}
func deleteEntry(forKey key: String) -> Bool {
store[key] = nil
return true
}
}
let credentialsManager = CredentialsManager(authentication: authentication,
storage: CustomStore())
This method now yields a Result<Credentials, CredentialsManagerError>
, which is aliased to CredentialsManagerResult<Credentials>
.
Before / After
// Before
credentialsManager.credentials { error, credentials in
guard error == nil, let credentials = credentials else {
// ...
return
}
// ...
}
// After
credentialsManager.credentials { result in
switch result {
case .success(let credentials): // ...
case .failure(let error): // ...
}
}
This method now yields a Result<Void, CredentialsManagerError>
, which is aliased to CredentialsManagerResult<Void>
.
Before / After
// Before
credentialsManager.revoke { error in
guard error == nil {
// ...
return
}
// ...
}
// After
credentialsManager.revoke { result in
switch result {
case .success: // ...
case .failure(let error): // ...
}
}
If you use the parameters(_:)
method of Request
to pass scopes and do not include the openid
scope, it will be added automatically.
Auth0
.authentication()
// ...
.parameters(["scope": "profile email"]) // "openid profile email" will be used
.start { result in
// ...
}
ID tokens signed with the HS256 algorithm are no longer allowed. This is because HS256 is a symmetric algorithm, which is not suitable for public clients like mobile applications. The only algorithm supported now is RS256, an asymmetric algorithm.
If your application is using HS256, you'll need to switch it to RS256 in the Dashboard or login will fail with an error:
Your Auth0 application settings > Advanced settings > JSON Web Token (JWT) Signature Algorithm
If the scopes passed via the Web Auth method .scope(_:)
do not include the openid
scope, it will be added automatically.
Auth0
.webAuth()
.scope("profile email") // "openid profile email" will be used
.start { result in
// ...
}
The ID token expiration is no longer used to determine if the credentials are still valid. Only the access token expiration is used now.
The hasValid(minTTL:)
method no longer returns true
if a refresh token is present. Now, only the access token expiration (along with the minTTL
value) determines the return value of hasValid(minTTL:)
.
Note that hasValid(minTTL:)
is no longer being called in credentials(withScope:minTTL:parameters:headers:callback:)
before the biometrics authentication. If you were relying on this behavior, you'll need to call hasValid(minTTL:)
before credentials(withScope:minTTL:parameters:headers:callback:)
yourself.
You'll also need to call hasValid(minTTL:)
before credentials(withScope:minTTL:parameters:headers:callback:)
yourself if you're not using refresh tokens. Otherwise, that method will now produce a CredentialsManagerError.noRefreshToken
error when the credentials are not valid and there is no refresh token available.
The method credentials(withScope:minTTL:parameters:headers:callback:)
now executes the credentials renewal serially, to prevent race conditions when refresh token rotation is enabled.