Add single/multi identity strategy to OIDC providers

OIDCProviders and OIDCMixin accept strategy='single-identity' (default) or 'multi-identity'. Single-identity evicts other providers' identities and tokens after a successful login so the session holds exactly one user, matching Spring Security's last-login-wins behavior. Ephemeral flow keys are preserved so in-flight logins can still complete.
This commit is contained in:
woggioni committed 2026-10-01 08:23:40 +08:00
1 parent 3af2e7a975
commit 15dc4b6dad
5 files changed
+224 -14

No files matched your search

+30 -7
View File
@@ -86,19 +86,41 @@ async def who(ctx: HttpContext):
await ctx.send_str(200, ', '.join(users))
```
Each provider's session state is namespaced as `oidc:{provider}:...`, so a user
can be logged into several providers at once. Logging out of one provider only
removes that provider's state and keeps the shared session alive while another
provider is still logged in. Make sure each config's `redirect_uri` points at
that provider's derived callback path.
Each provider's session state is namespaced as `oidc:{provider}:...`. Make sure
each config's `redirect_uri` points at that provider's derived callback path.
### Identity strategy
`strategy` controls how many identities may coexist in the shared session:
- `'single-identity'` (default): completing a login evicts every other
provider's identity and tokens, so the session holds exactly one user — last
login wins, like Spring Security. After a GitHub login, `get_user(ctx,
'google')` returns `None` and `require_auth('google')` redirects to the Google
login.
- `'multi-identity'`: each provider stays logged in independently. Use it for
linked accounts, aggregating several provider APIs, or step-up flows. Logging
out of one provider keeps the shared session alive while another provider is
still logged in.
```python
oidc = OIDCProviders([google_config, github_config], session=session,
strategy='multi-identity')
```
Ephemeral flow keys (`state`, `nonce`, `code_verifier`) are never evicted, so
logins already in flight can still complete; under `'single-identity'` the last
callback to finish wins. Under either strategy, `get_users(ctx)` only reports
providers actually present in the session.
The composable alternative is to create one `OIDCMixin` per provider, pointing
each at distinct `login_path`/`callback_path`/`logout_path` values and sharing
the same `SessionMixin`:
the same `SessionMixin`. The same `strategy` keyword is accepted and defaults to
`'single-identity'`:
```python
google = OIDCMixin(google_config, session=session)
github = OIDCMixin(github_config, session=session)
github = OIDCMixin(github_config, session=session, strategy='multi-identity')
app = KayaApp(mixins=[session, google, github])
```
@@ -117,6 +139,7 @@ app = KayaApp(mixins=[session, google, github])
- Refresh token support
- RP-initiated logout (when provider advertises `end_session_endpoint`)
- Multiple providers in one app via `OIDCProviders` (or several `OIDCMixin` instances)
- `single-identity` (default) or `multi-identity` session strategy
- Composable with any other `KayaMixin` (RSGI, MCP, etc.)
## Security notes