Skip to content
Open
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
18 changes: 14 additions & 4 deletions Auth0/CredentialsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public struct CredentialsManager {
private let storage: CredentialsStorage
private let storeKey: String
private let authentication: Authentication
private let allowsAutoRefreshing: Bool
private let dispatchQueue = DispatchQueue(label: "com.auth0.credentialsmanager.serial")
#if WEB_AUTH_PLATFORM
var bioAuth: BioAuthentication?
Expand All @@ -40,12 +41,15 @@ public struct CredentialsManager {
/// - authentication: Auth0 Authentication API client.
/// - storeKey: Key used to store user credentials in the Keychain. Defaults to 'credentials'.
/// - storage: The ``CredentialsStorage`` instance used to manage credentials storage. Defaults to a standard `SimpleKeychain` instance.
/// - allowsAutoRefreshing: If `true` (the default), `CredentialsManager` will automatically attempt to refresh credentials using a refresh token.
public init(authentication: Authentication,
storeKey: String = "credentials",
storage: CredentialsStorage = SimpleKeychain()) {
storage: CredentialsStorage = SimpleKeychain(),
allowsAutoRefreshing: Bool = true) {
self.storeKey = storeKey
self.authentication = authentication
self.storage = storage
self.allowsAutoRefreshing = allowsAutoRefreshing
}

/// Retrieves the user information from the Keychain synchronously, without checking if the credentials are expired.
Expand Down Expand Up @@ -240,12 +244,13 @@ public struct CredentialsManager {
/// - Returns: If there are credentials stored containing a refresh token.
public func canRenew() -> Bool {
guard let credentials = self.retrieveCredentials() else { return false }
return credentials.refreshToken != nil
return self.allowsAutoRefreshing && credentials.refreshToken != nil

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe check allowsAutoRefreshing before self.retrieveCredentials() ?

}

#if WEB_AUTH_PLATFORM
/// Retrieves credentials from the Keychain and automatically renews them using the refresh token if the access
/// token is expired. Otherwise, the retrieved credentials will be returned via the success case as they are still
/// Retrieves credentials from the Keychain and automatically renews them (if `allowsAutoRefreshing` is true)
/// using the refresh token if the access token is expired.
/// Otherwise, the retrieved credentials will be returned via the success case as they are still
/// valid. Renewed credentials will be stored in the Keychain. **This method is thread-safe**.
///
/// ## Usage
Expand Down Expand Up @@ -652,6 +657,11 @@ public struct CredentialsManager {
dispatchGroup.leave()
return callback(.success(credentials))
}

guard self.allowsAutoRefreshing else {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is right. This will prevent refreshing, but that's not the only thing the code below does, it also tries to read the credentials from the store/keychain

dispatchGroup.leave()
return callback(.failure(.renewNotSupported))
}
guard let refreshToken = credentials.refreshToken else {
dispatchGroup.leave()
return callback(.failure(.noRefreshToken))
Expand Down
6 changes: 6 additions & 0 deletions Auth0/CredentialsManagerError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public struct CredentialsManagerError: Auth0Error, Sendable {
case noCredentials
case noRefreshToken
case renewFailed
case renewNotSupported
case apiExchangeFailed
case ssoExchangeFailed
case storeFailed
Expand Down Expand Up @@ -46,6 +47,10 @@ public struct CredentialsManagerError: Auth0Error, Sendable {
/// The underlying ``AuthenticationError`` can be accessed via the ``Auth0Error/cause-9wuyi`` property.
public static let renewFailed: CredentialsManagerError = .init(code: .renewFailed)

/// The credentials renewal is not supported.
/// The underlying ``AuthenticationError`` can be accessed via the ``Auth0Error/cause-9wuyi`` property.
public static let renewNotSupported: CredentialsManagerError = .init(code: .renewNotSupported)

/// The exchange of the refresh token for API credentials failed.
/// The underlying ``AuthenticationError`` can be accessed via the ``Auth0Error/cause-9wuyi`` property.
public static let apiExchangeFailed: CredentialsManagerError = .init(code: .apiExchangeFailed)
Expand Down Expand Up @@ -82,6 +87,7 @@ extension CredentialsManagerError {
case .noCredentials: return "No credentials were found in the store."
case .noRefreshToken: return "The stored credentials instance does not contain a refresh token."
case .renewFailed: return "The credentials renewal failed."
case .renewNotSupported: return "Credentials renewal is disabled."
case .apiExchangeFailed: return "The exchange of the refresh token for API credentials failed."
case .ssoExchangeFailed: return "The exchange of the refresh token for SSO credentials failed."
case .storeFailed: return "Storing the renewed credentials failed."
Expand Down
6 changes: 6 additions & 0 deletions Auth0Tests/CredentialsManagerErrorSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ class CredentialsManagerErrorSpec: QuickSpec {
expect(error.localizedDescription) == message
}

it("should return message for renew failed") {
let message = "Credentials renewal is disabled."
let error = CredentialsManagerError(code: .renewNotSupported)
expect(error.localizedDescription) == message
}

it("should return message for API exchange failed") {
let message = "The exchange of the refresh token for API credentials failed."
let error = CredentialsManagerError(code: .apiExchangeFailed)
Expand Down
15 changes: 15 additions & 0 deletions Auth0Tests/CredentialsManagerSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,21 @@ class CredentialsManagerSpec: QuickSpec {
}
}
}

it("should not renew if not enabled") {
credentialsManager = CredentialsManager(authentication: authentication,
storage: SimpleKeychain(),
allowsAutoRefreshing: false)
credentials = Credentials(accessToken: AccessToken, tokenType: TokenType, idToken: IdToken, refreshToken: RefreshToken, expiresIn: Date(timeIntervalSinceNow: -ExpiresIn))
_ = credentialsManager.store(credentials: credentials)

waitUntil(timeout: Timeout) { done in
credentialsManager.credentials { result in
expect(result).to(haveCredentialsManagerError(.renewNotSupported))
done()
}
}
}
}

context("forced renewal of credentials") {
Expand Down