Skip to content
64 changes: 49 additions & 15 deletions Auth0/CredentialsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,12 @@ public struct CredentialsManager {
/// ```
///
/// - Parameter audience: Identifier of the API the stored API credentials are for.
/// - Parameter scope: Optional scope for which the API Credentials are stored. If the credentials were initially fetched/stored with scope,
/// it is recommended to pass scope also while clearing them.
/// - Returns: If the API credentials were removed.
public func clear(forAudience audience: String) -> Bool {
return self.storage.deleteEntry(forKey: audience)
public func clear(forAudience audience: String,scope:String? = nil) -> Bool {
let key = getAPICredentialsStorageKey(audience: audience, scope: scope)
return self.storage.deleteEntry(forKey: key)
}

#if WEB_AUTH_PLATFORM
Expand Down Expand Up @@ -694,23 +697,38 @@ public struct CredentialsManager {
callback: callback)
}

public func store(apiCredentials: APICredentials, forAudience audience: String) -> Bool {
public func store(apiCredentials: APICredentials, forAudience audience: String, forScope scope: String? = nil) -> Bool {
guard let data = try? apiCredentials.encode() else {
return false
}

return self.storage.setEntry(data, forKey: audience)
let key = getAPICredentialsStorageKey(audience: audience, scope: scope)
return self.storage.setEntry(data, forKey: key)
}

private func retrieveCredentials() -> Credentials? {
guard let data = self.storage.getEntry(forKey: self.storeKey) else { return nil }
return try? NSKeyedUnarchiver.unarchivedObject(ofClass: Credentials.self, from: data)
}

private func retrieveAPICredentials(audience: String) -> APICredentials? {
guard let data = self.storage.getEntry(forKey: audience) else { return nil }
private func retrieveAPICredentials(audience: String,scope: String?) -> APICredentials? {
let key = getAPICredentialsStorageKey(audience: audience, scope: scope)
guard let data = self.storage.getEntry(forKey: key) else { return nil }
return try? APICredentials(from: data)
}

private func getAPICredentialsStorageKey(audience: String, scope: String?) -> String {
// Use audience if scope is null else use a combination of audience and scope
if let scope = scope {
let normalisedScopes = scope
.split(separator: " ")
.sorted()
.joined(separator: "::")
return "\(audience)::\(normalisedScopes)"
} else {
return audience
}
}

// swiftlint:disable:next function_parameter_count
private func retrieveCredentials(scope: String?,
Expand Down Expand Up @@ -832,10 +850,10 @@ public struct CredentialsManager {
dispatchGroup.enter()

DispatchQueue.global(qos: .userInitiated).async {
if let apiCredentials = self.retrieveAPICredentials(audience: audience),
if let apiCredentials = self.retrieveAPICredentials(audience: audience,scope: scope),
!self.hasExpired(apiCredentials.expiresIn),
!self.willExpire(apiCredentials.expiresIn, within: minTTL),
!self.hasScopeChanged(from: apiCredentials.scope, to: scope) {
!self.hasScopeChanged(from: apiCredentials.scope, to: scope, ignoreOpenid: scope?.contains("openid") == false) {
dispatchGroup.leave()
return callback(.success(apiCredentials))
}
Expand Down Expand Up @@ -867,7 +885,7 @@ public struct CredentialsManager {
} else if !self.store(credentials: newCredentials) {
dispatchGroup.leave()
callback(.failure(CredentialsManagerError(code: .storeFailed)))
} else if !self.store(apiCredentials: newAPICredentials, forAudience: audience) {
} else if !self.store(apiCredentials: newAPICredentials, forAudience: audience, forScope: scope) {
dispatchGroup.leave()
callback(.failure(CredentialsManagerError(code: .storeFailed)))
} else {
Expand All @@ -893,14 +911,30 @@ public struct CredentialsManager {
return expiresIn < Date()
}

func hasScopeChanged(from lastScope: String?, to newScope: String?) -> Bool {
func hasScopeChanged(from lastScope: String?, to newScope: String?, ignoreOpenid: Bool = false) -> Bool {

if let lastScope = lastScope, let newScope = newScope {
let lastScopeList = lastScope.lowercased().split(separator: " ").sorted()
let newScopeList = newScope.lowercased().split(separator: " ").sorted()

return lastScopeList != newScopeList

var storedScopes = Set(
lastScope
.split(separator: " ")
.filter { !$0.isEmpty }
.map { String($0).lowercased() }
)

if ignoreOpenid {
storedScopes.remove("openid")
}

let requiredScopes = Set(
newScope
.split(separator: " ")
.filter { !$0.isEmpty }
.map { String($0).lowercased() }
)

return storedScopes != requiredScopes
}

return false
}

Expand Down
Loading