Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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 @@
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 @@
/// - 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 @@
/// - 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 @@ -629,7 +634,7 @@
}

// swiftlint:disable:next function_parameter_count
private func retrieveCredentials(scope: String?,

Check warning on line 637 in Auth0/CredentialsManager.swift

View workflow job for this annotation

GitHub Actions / Lint code with SwiftLint

Function body should span 50 lines or less excluding comments and whitespace: currently spans 52 lines (function_body_length)
minTTL: Int,
parameters: [String: Any],
headers: [String: String],
Expand All @@ -652,6 +657,11 @@
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
Loading