(self, event, now=None)
| 336 | return {}, None |
| 337 | |
| 338 | def __add(self, event, now=None): |
| 339 | # event typically contains: client_id, scope, token_endpoint, |
| 340 | # response, params, data, grant_type |
| 341 | environment = realm = None |
| 342 | if "token_endpoint" in event: |
| 343 | _, environment, realm = canonicalize(event["token_endpoint"]) |
| 344 | if "environment" in event: # Always available unless in legacy test cases |
| 345 | environment = event["environment"] # Set by application.py |
| 346 | response = event.get("response", {}) |
| 347 | data = event.get("data", {}) |
| 348 | access_token = response.get("access_token") |
| 349 | refresh_token = response.get("refresh_token") |
| 350 | id_token = response.get("id_token") |
| 351 | id_token_claims = response.get("id_token_claims") or ( # Prefer the claims from broker |
| 352 | # Only use decode_id_token() when necessary, it contains time-sensitive validation |
| 353 | decode_id_token(id_token, client_id=event["client_id"]) if id_token else {}) |
| 354 | client_info, home_account_id = self.__parse_account(response, id_token_claims) |
| 355 | |
| 356 | target = ' '.join(sorted(event.get("scope") or [])) # Schema should have required sorting |
| 357 | |
| 358 | with self._lock: |
| 359 | now = int(time.time() if now is None else now) |
| 360 | |
| 361 | if access_token: |
| 362 | default_expires_in = ( # https://www.rfc-editor.org/rfc/rfc6749#section-5.1 |
| 363 | int(response.get("expires_on")) - now # Some Managed Identity emits this |
| 364 | ) if response.get("expires_on") else 600 |
| 365 | expires_in = int( # AADv1-like endpoint returns a string |
| 366 | response.get("expires_in", default_expires_in)) |
| 367 | ext_expires_in = int( # AADv1-like endpoint returns a string |
| 368 | response.get("ext_expires_in", expires_in)) |
| 369 | at = { |
| 370 | "credential_type": self.CredentialType.ACCESS_TOKEN, |
| 371 | "secret": access_token, |
| 372 | "home_account_id": home_account_id, |
| 373 | "environment": environment, |
| 374 | "client_id": event.get("client_id"), |
| 375 | "target": target, |
| 376 | "realm": realm, |
| 377 | "token_type": response.get("token_type", "Bearer"), |
| 378 | "cached_at": str(now), # Schema defines it as a string |
| 379 | "expires_on": str(now + expires_in), # Same here |
| 380 | "extended_expires_on": str(now + ext_expires_in) # Same here |
| 381 | } |
| 382 | at.update({k: data[k] for k in data if k in { |
| 383 | # Also store extra data which we explicitly allow |
| 384 | # So that we won't accidentally store a user's password etc. |
| 385 | "key_id", # It happens in SSH-cert or POP scenario |
| 386 | }}) |
| 387 | # Compute and store extended cache key for cache isolation |
| 388 | # (e.g., different FMI paths should have separate cache entries) |
| 389 | ext_cache_key = _compute_ext_cache_key(data) |
| 390 | |
| 391 | if ext_cache_key: |
| 392 | at["ext_cache_key"] = ext_cache_key |
| 393 | if "refresh_in" in response: |
| 394 | refresh_in = response["refresh_in"] # It is an integer |
| 395 | at["refresh_on"] = str(now + refresh_in) # Schema wants a string |
no test coverage detected