@@ -38,23 +38,55 @@ class OSIdentityModel: OSModel {
3838 return internalGetAlias ( OS_EXTERNAL_ID)
3939 }
4040
41- // All access to aliases should go through helper methods with locking
41+ // All access to aliases and jwtBearerToken must go through the lock
4242 var aliases : [ String : String ] = [ : ]
43- private let aliasesLock = NSRecursiveLock ( )
43+ private let lock = NSRecursiveLock ( )
4444
4545 // MARK: - JWT
4646
47+ private var jwtBearerTokenLocked : String ? // only read/write under self.lock
4748 public var jwtBearerToken : String ? {
48- didSet {
49- guard jwtBearerToken != oldValue else {
50- return
49+ get {
50+ lock. withLock { jwtBearerTokenLocked }
51+ }
52+ set {
53+ // Lock only the storage write. The change notifier fires synchronously
54+ // to listeners that may take other locks
55+ let changed = lock. withLock {
56+ guard newValue != jwtBearerTokenLocked else { return false }
57+ jwtBearerTokenLocked = newValue
58+ return true
59+ }
60+ if changed {
61+ self . set ( property: OS_JWT_BEARER_TOKEN, newValue: newValue)
5162 }
52- self . set ( property: OS_JWT_BEARER_TOKEN, newValue: jwtBearerToken)
5363 }
5464 }
5565
56- func isJwtValid( ) -> Bool {
57- return jwtBearerToken != nil && jwtBearerToken != " " && jwtBearerToken != OS_JWT_TOKEN_INVALID
66+ /// Returns the bearer token if it is valid, otherwise nil, snapshots once
67+ func getValidJwt( ) -> String ? {
68+ let token = jwtBearerToken
69+ guard let token = token, !token. isEmpty, token != OS_JWT_TOKEN_INVALID else {
70+ return nil
71+ }
72+ return token
73+ }
74+
75+ /**
76+ Atomically transition the JWT token to `OS_JWT_TOKEN_INVALID`. Returns
77+ `true` if the transition occurred, `false` if the token was already invalid.
78+ */
79+ @discardableResult
80+ func invalidateJwtBearerToken( ) -> Bool {
81+ let changed = lock. withLock {
82+ guard jwtBearerTokenLocked != OS_JWT_TOKEN_INVALID else { return false }
83+ jwtBearerTokenLocked = OS_JWT_TOKEN_INVALID
84+ return true
85+ }
86+ if changed {
87+ self . set ( property: OS_JWT_BEARER_TOKEN, newValue: OS_JWT_TOKEN_INVALID)
88+ }
89+ return changed
5890 }
5991
6092 // MARK: - Initialization
@@ -66,10 +98,10 @@ class OSIdentityModel: OSModel {
6698 }
6799
68100 override func encode( with coder: NSCoder ) {
69- aliasesLock . withLock {
101+ lock . withLock {
70102 super. encode ( with: coder)
71103 coder. encode ( aliases, forKey: " aliases " )
72- coder. encode ( jwtBearerToken , forKey: OS_JWT_BEARER_TOKEN)
104+ coder. encode ( jwtBearerTokenLocked , forKey: OS_JWT_BEARER_TOKEN)
73105 }
74106 }
75107
@@ -79,20 +111,20 @@ class OSIdentityModel: OSModel {
79111 // log error
80112 return nil
81113 }
82- self . jwtBearerToken = coder. decodeObject ( forKey: OS_JWT_BEARER_TOKEN) as? String
114+ self . jwtBearerTokenLocked = coder. decodeObject ( forKey: OS_JWT_BEARER_TOKEN) as? String
83115 self . aliases = aliases
84116 }
85117
86118 /** Threadsafe getter for an alias */
87119 private func internalGetAlias( _ label: String ) -> String ? {
88- aliasesLock . withLock {
120+ lock . withLock {
89121 return self . aliases [ label]
90122 }
91123 }
92124
93125 /** Threadsafe setter or removal for aliases */
94126 private func internalAddAliases( _ aliases: [ String : String ] ) {
95- aliasesLock . withLock {
127+ lock . withLock {
96128 for (label, id) in aliases {
97129 // Remove the alias if the ID field is ""
98130 self . aliases [ label] = id. isEmpty ? nil : id
@@ -105,7 +137,7 @@ class OSIdentityModel: OSModel {
105137 Called to clear the model's data in preparation for hydration via a fetch user call.
106138 */
107139 func clearData( ) {
108- aliasesLock . withLock {
140+ lock . withLock {
109141 self . aliases = [ : ]
110142 }
111143 }
0 commit comments