Metadata-Version: 2.4
Name: oidcauthlib
Version: 3.0.25
Summary: oidcauthlib
Author-email: Imran Qureshi <imran.qureshi@bwell.com>
License: Apache-2.0
Project-URL: Homepage, https://github.com/icanbwell/oidc-auth-lib
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Classifier: License :: OSI Approved :: Apache Software License
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.27.2
Requires-Dist: httpx2>=2.12.0
Requires-Dist: authlib>=1.8.0
Requires-Dist: joserfc>=1.4.3
Requires-Dist: pydantic<3.0.0,>=2.0
Requires-Dist: pymongo[snappy]>=4.15.3
Requires-Dist: python-snappy>=0.7.3
Requires-Dist: fastapi>=0.115.8
Requires-Dist: starlette>=0.49.1
Requires-Dist: py-key-value-aio[memory,mongodb,pydantic,redis]>=0.4.4
Requires-Dist: opentelemetry-api>=1.39.1
Requires-Dist: simple-container>=1.0.2
Dynamic: license-file

# oidcauthlib

## OIDC userinfo scopes (`OidcUserInfoManager`)

Some providers (e.g. Okta employee access tokens) only carry `openid profile email groups` in the JWT;
the scopes that authorize a caller appear only on the OIDC userinfo response.
`oidcauthlib.auth.userinfo.oidc_userinfo_manager.OidcUserInfoManager` resolves a token's effective
scopes from the token's own claims plus the userinfo endpoint, and caches the userinfo-derived scopes in
a key-value store.

```python
manager = OidcUserInfoManager(
    auth_config_reader=auth_config_reader,
    well_known_configuration_manager=well_known_configuration_manager,
    store=store,                      # py-key-value-aio store; yours, not shared with other services
    cache_collection="oidc-user-info-cache",
    cache_ttl_seconds=86400,
    fetch_timeout_seconds=30,
    scope_claims=["custom:scope"],    # optional extra claims holding scopes
    group_claims=["groups"],          # optional claims holding group names, used whole as scopes
    scope_remove_prefixes=["fhir/prod/"],
)
scopes = await manager.get_scopes_from_user_info_async(access_token=token)
```

- **Scope derivation** follows fhir-server-ui's `jwtParser`: the `scope` claim (plus `scp`), then
  `scope_claims`, then `group_claims` (each value taken whole). Commas separate like spaces, results are
  de-duplicated, and `scope_remove_prefixes` are stripped (first match wins). Nothing beyond `scope`/`scp`
  is read unless configured.
- **Provider** is matched to the token's `iss`: an exact `issuer`, else a `well_known_uri` on the same scheme,
  host and port whose path segments start with the issuer's. Only a token with no `iss` at all is matched by
  its client id. A token matching no provider raises `ValueError`; it is never sent to another provider.
- **Subject check:** for a token that has a `sub`, the userinfo response must carry the same `sub`
  (OIDC Core 5.3.2); a missing or different one raises `ValueError` and nothing is cached.
- **Cache** key is `oidc-userinfo-scopes:<issuer>:<client_id>:<sub>|v1` with each segment percent-encoded
  (so values containing `:` cannot collide, and two providers never share an entry); only the
  userinfo-derived scopes are stored, never the userinfo response. The token's own scopes are re-derived on every call. A token without
  `sub` or without an issuer, or an empty scope list, is not cached. Changes to a user's entitlements are visible after at most
  `cache_ttl_seconds`.
- **Failures:** the cache never fails a lookup. A write failure is logged and swallowed; a read failure or a
  malformed entry is logged and treated as a miss, and the refetched scopes overwrite the bad entry. A
  userinfo HTTP error, a missing provider or a mismatched subject is raised, so each service decides whether
  to fail closed or degrade.
