@@ -37,7 +37,7 @@ public struct CredentialsManager {
3737 let noSession : TimeInterval = - 1
3838 var lastBiometricAuthTime : TimeInterval = - 1
3939 let lock = NSLock ( )
40-
40+
4141 init ( ) {
4242 lastBiometricAuthTime = noSession
4343 }
@@ -163,9 +163,12 @@ public struct CredentialsManager {
163163 /// ```
164164 ///
165165 /// - Parameter audience: Identifier of the API the stored API credentials are for.
166+ /// - Parameter scope: Optional scope for which the API Credentials are stored. If the credentials were initially fetched/stored with scope,
167+ /// it is recommended to pass scope also while clearing them.
166168 /// - Returns: If the API credentials were removed.
167- public func clear( forAudience audience: String ) -> Bool {
168- return self . storage. deleteEntry ( forKey: audience)
169+ public func clear( forAudience audience: String , scope: String ? = nil ) -> Bool {
170+ let key = getAPICredentialsStorageKey ( audience: audience, scope: scope)
171+ return self . storage. deleteEntry ( forKey: key)
169172 }
170173
171174 #if WEB_AUTH_PLATFORM
@@ -180,13 +183,13 @@ public struct CredentialsManager {
180183 /// - Returns: `true` if the session is valid and biometric authentication can be skipped, `false` otherwise.
181184 public func isBiometricSessionValid( ) -> Bool {
182185 guard let bioAuth = self . bioAuth else { return false }
183-
186+
184187 self . biometricSession. lock. lock ( )
185188 defer { self . biometricSession. lock. unlock ( ) }
186-
189+
187190 let lastAuth = self . biometricSession. lastBiometricAuthTime
188191 if lastAuth == self . biometricSession. noSession { return false }
189-
192+
190193 switch bioAuth. policy {
191194 case . session( let timeoutInSeconds) , . appLifecycle( let timeoutInSeconds) :
192195 let timeoutInterval = TimeInterval ( timeoutInSeconds)
@@ -694,24 +697,39 @@ public struct CredentialsManager {
694697 callback: callback)
695698 }
696699
697- public func store( apiCredentials: APICredentials , forAudience audience: String ) -> Bool {
700+ public func store( apiCredentials: APICredentials , forAudience audience: String , forScope scope : String ? = nil ) -> Bool {
698701 guard let data = try ? apiCredentials. encode ( ) else {
699702 return false
700703 }
701704
702- return self . storage. setEntry ( data, forKey: audience)
705+ let key = getAPICredentialsStorageKey ( audience: audience, scope: scope)
706+ return self . storage. setEntry ( data, forKey: key)
703707 }
704708
705709 private func retrieveCredentials( ) -> Credentials ? {
706710 guard let data = self . storage. getEntry ( forKey: self . storeKey) else { return nil }
707711 return try ? NSKeyedUnarchiver . unarchivedObject ( ofClass: Credentials . self, from: data)
708712 }
709713
710- private func retrieveAPICredentials( audience: String ) -> APICredentials ? {
711- guard let data = self . storage. getEntry ( forKey: audience) else { return nil }
714+ private func retrieveAPICredentials( audience: String , scope: String ? ) -> APICredentials ? {
715+ let key = getAPICredentialsStorageKey ( audience: audience, scope: scope)
716+ guard let data = self . storage. getEntry ( forKey: key) else { return nil }
712717 return try ? APICredentials ( from: data)
713718 }
714719
720+ private func getAPICredentialsStorageKey( audience: String , scope: String ? ) -> String {
721+ // Use audience if scope is null else use a combination of audience and scope
722+ if let scope = scope {
723+ let normalisedScopes = scope
724+ . split ( separator: " " )
725+ . sorted ( )
726+ . joined ( separator: " :: " )
727+ return " \( audience) :: \( normalisedScopes) "
728+ } else {
729+ return audience
730+ }
731+ }
732+
715733 // swiftlint:disable:next function_parameter_count
716734 private func retrieveCredentials( scope: String ? ,
717735 minTTL: Int ,
@@ -832,10 +850,10 @@ public struct CredentialsManager {
832850 dispatchGroup. enter ( )
833851
834852 DispatchQueue . global ( qos: . userInitiated) . async {
835- if let apiCredentials = self . retrieveAPICredentials ( audience: audience) ,
853+ if let apiCredentials = self . retrieveAPICredentials ( audience: audience, scope : scope ) ,
836854 !self . hasExpired ( apiCredentials. expiresIn) ,
837855 !self . willExpire ( apiCredentials. expiresIn, within: minTTL) ,
838- !self . hasScopeChanged ( from: apiCredentials. scope, to: scope) {
856+ !self . hasScopeChanged ( from: apiCredentials. scope, to: scope, ignoreOpenid : scope ? . contains ( " openid " ) == false ) {
839857 dispatchGroup. leave ( )
840858 return callback ( . success( apiCredentials) )
841859 }
@@ -867,7 +885,7 @@ public struct CredentialsManager {
867885 } else if !self . store ( credentials: newCredentials) {
868886 dispatchGroup. leave ( )
869887 callback ( . failure( CredentialsManagerError ( code: . storeFailed) ) )
870- } else if !self . store ( apiCredentials: newAPICredentials, forAudience: audience) {
888+ } else if !self . store ( apiCredentials: newAPICredentials, forAudience: audience, forScope : scope ) {
871889 dispatchGroup. leave ( )
872890 callback ( . failure( CredentialsManagerError ( code: . storeFailed) ) )
873891 } else {
@@ -893,14 +911,30 @@ public struct CredentialsManager {
893911 return expiresIn < Date ( )
894912 }
895913
896- func hasScopeChanged( from lastScope: String ? , to newScope: String ? ) -> Bool {
914+ func hasScopeChanged( from lastScope: String ? , to newScope: String ? , ignoreOpenid: Bool = false ) -> Bool {
915+
897916 if let lastScope = lastScope, let newScope = newScope {
898- let lastScopeList = lastScope. lowercased ( ) . split ( separator: " " ) . sorted ( )
899- let newScopeList = newScope. lowercased ( ) . split ( separator: " " ) . sorted ( )
900917
901- return lastScopeList != newScopeList
902- }
918+ var storedScopes = Set (
919+ lastScope
920+ . split ( separator: " " )
921+ . filter { !$0. isEmpty }
922+ . map { String ( $0) . lowercased ( ) }
923+ )
903924
925+ if ignoreOpenid {
926+ storedScopes. remove ( " openid " )
927+ }
928+
929+ let requiredScopes = Set (
930+ newScope
931+ . split ( separator: " " )
932+ . filter { !$0. isEmpty }
933+ . map { String ( $0) . lowercased ( ) }
934+ )
935+
936+ return storedScopes != requiredScopes
937+ }
904938 return false
905939 }
906940
0 commit comments