@@ -38,13 +38,19 @@ def __init__(self):
3838 def find (self , credential_type , target = None , query = None ):
3939 target = target or []
4040 assert isinstance (target , list ), "Invalid parameter type"
41+ target_set = set (target )
4142 with self ._lock :
43+ # Since the target inside token cache key is (per schema) unsorted,
44+ # there is no point to attempt an O(1) key-value search here.
45+ # So we always do an O(n) in-memory search.
4246 return [entry
4347 for entry in self ._cache .get (credential_type , {}).values ()
4448 if is_subdict_of (query or {}, entry )
45- and set (target ) <= set (entry .get ("target" , []))]
49+ and (target_set <= set (entry .get ("target" , "" ).split ())
50+ if target else True )
51+ ]
4652
47- def add (self , event ):
53+ def add (self , event , now = None ):
4854 # type: (dict) -> None
4955 # event typically contains: client_id, scope, token_endpoint,
5056 # resposne, params, data, grant_type
@@ -56,9 +62,9 @@ def add(self, event):
5662 default = str , # A workaround when assertion is in bytes in Python 3
5763 ))
5864 response = event .get ("response" , {})
59- access_token = response .get ("access_token" , {} )
60- refresh_token = response .get ("refresh_token" , {} )
61- id_token = response .get ("id_token" , {} )
65+ access_token = response .get ("access_token" )
66+ refresh_token = response .get ("refresh_token" )
67+ id_token = response .get ("id_token" )
6268 client_info = {}
6369 home_account_id = None
6470 if "client_info" in response :
@@ -67,6 +73,7 @@ def add(self, event):
6773 environment = realm = None
6874 if "token_endpoint" in event :
6975 _ , environment , realm = canonicalize (event ["token_endpoint" ])
76+ target = ' ' .join (event .get ("scope" , [])) # Per schema, we don't sort it
7077
7178 with self ._lock :
7279
@@ -77,20 +84,22 @@ def add(self, event):
7784 self .CredentialType .ACCESS_TOKEN ,
7885 event .get ("client_id" , "" ),
7986 realm or "" ,
80- ' ' . join ( sorted ( event . get ( "scope" , []))) ,
87+ target ,
8188 ]).lower ()
82- now = time .time ()
89+ now = time .time () if now is None else now
90+ expires_in = response .get ("expires_in" , 3599 )
8391 self ._cache .setdefault (self .CredentialType .ACCESS_TOKEN , {})[key ] = {
8492 "credential_type" : self .CredentialType .ACCESS_TOKEN ,
8593 "secret" : access_token ,
8694 "home_account_id" : home_account_id ,
8795 "environment" : environment ,
8896 "client_id" : event .get ("client_id" ),
89- "target" : event . get ( "scope" ) ,
97+ "target" : target ,
9098 "realm" : realm ,
91- "cached_at" : now ,
92- "expires_on" : now + response .get ("expires_in" , 3599 ),
93- "extended_expires_on" : now + response .get ("ext_expires_in" , 0 ),
99+ "cached_at" : str (int (now )), # Schema defines it as a string
100+ "expires_on" : str (int (now + expires_in )), # Same here
101+ "extended_expires_on" : str (int ( # Same here
102+ now + response .get ("ext_expires_in" , expires_in ))),
94103 }
95104
96105 if client_info :
@@ -108,7 +117,10 @@ def add(self, event):
108117 "local_account_id" : decoded_id_token .get (
109118 "oid" , decoded_id_token .get ("sub" )),
110119 "username" : decoded_id_token .get ("preferred_username" ),
111- "authority_type" : "AAD" , # Always AAD?
120+ "authority_type" :
121+ "ADFS" if realm == "adfs"
122+ else "MSSTS" , # MSSTS means AAD v2 for both AAD & MSA
123+ # "client_info": response.get("client_info"), # Optional
112124 }
113125
114126 if id_token :
@@ -118,6 +130,7 @@ def add(self, event):
118130 self .CredentialType .ID_TOKEN ,
119131 event .get ("client_id" , "" ),
120132 realm or "" ,
133+ "" # Albeit irrelevant, schema requires an empty scope here
121134 ]).lower ()
122135 self ._cache .setdefault (self .CredentialType .ID_TOKEN , {})[key ] = {
123136 "credential_type" : self .CredentialType .ID_TOKEN ,
@@ -132,16 +145,14 @@ def add(self, event):
132145 if refresh_token :
133146 key = self ._build_rt_key (
134147 home_account_id , environment ,
135- event .get ("client_id" , "" ), event . get ( "scope" , []) )
148+ event .get ("client_id" , "" ), target )
136149 rt = {
137150 "credential_type" : self .CredentialType .REFRESH_TOKEN ,
138151 "secret" : refresh_token ,
139152 "home_account_id" : home_account_id ,
140153 "environment" : environment ,
141154 "client_id" : event .get ("client_id" ),
142- # Fields below are considered optional
143- "target" : event .get ("scope" ),
144- "client_info" : response .get ("client_info" ),
155+ "target" : target , # Optional per schema though
145156 }
146157 if "foci" in response :
147158 rt ["family_id" ] = response ["foci" ]
@@ -158,7 +169,7 @@ def _build_rt_key(
158169 cls .CredentialType .REFRESH_TOKEN ,
159170 client_id or "" ,
160171 "" , # RT is cross-tenant in AAD
161- ' ' . join ( sorted ( target or [])),
172+ target or "" , # raw value could be None if deserialized from other SDK
162173 ]).lower ()
163174
164175 def remove_rt (self , rt_item ):
@@ -169,7 +180,8 @@ def remove_rt(self, rt_item):
169180 def update_rt (self , rt_item , new_rt ):
170181 key = self ._build_rt_key (** rt_item )
171182 with self ._lock :
172- rt = self ._cache .setdefault (self .CredentialType .REFRESH_TOKEN , {})[key ]
183+ RTs = self ._cache .setdefault (self .CredentialType .REFRESH_TOKEN , {})
184+ rt = RTs .get (key , {}) # key usually exists, but we'll survive its absence
173185 rt ["secret" ] = new_rt
174186
175187
@@ -195,8 +207,8 @@ class SerializableTokenCache(TokenCache):
195207 Indicates whether the cache state has changed since last
196208 :func:`~serialize` or :func:`~deserialize` call.
197209 """
198- def add (self , event ):
199- super (SerializableTokenCache , self ).add (event )
210+ def add (self , event , ** kwargs ):
211+ super (SerializableTokenCache , self ).add (event , ** kwargs )
200212 self .has_state_changed = True
201213
202214 def remove_rt (self , rt_item ):
@@ -219,5 +231,5 @@ def serialize(self):
219231 """Serialize the current cache state into a string."""
220232 with self ._lock :
221233 self .has_state_changed = False
222- return json .dumps (self ._cache )
234+ return json .dumps (self ._cache , indent = 4 )
223235
0 commit comments