Refactor to composable KayaMixin architecture

Replace wrapper-based SessionMiddleware/OIDCApp with KayaMixin subclasses
applied via KayaApp(mixins=[...]). Mixins hook into handle_request and
handle_websocket via before/after hooks, so both ASGI and RSGI keep working.
Mixin dependencies are applied automatically and deduplicated.
This commit is contained in:
2026-07-23 22:09:59 +08:00
parent 24a797e3d2
commit 3ebf079533
14 changed files with 480 additions and 359 deletions
+13 -10
View File
@@ -10,34 +10,36 @@ Flow with PKCE**.
```python
import os
from kaya.core import HttpContext, KayaApp
from kaya.session import SessionMiddleware, InMemorySessionStore
from kaya.oidc import OIDCConfig, OIDCApp
from kaya.session import SessionMixin, InMemorySessionStore
from kaya.oidc import OIDCConfig, OIDCMixin
app = KayaApp()
session_app = SessionMiddleware(app, InMemorySessionStore())
oidc = OIDCApp(
session_app,
session = SessionMixin(InMemorySessionStore())
oidc = OIDCMixin(
OIDCConfig(
issuer=os.environ['OIDC_ISSUER'],
client_id=os.environ['OIDC_CLIENT_ID'],
client_secret=os.environ.get('OIDC_CLIENT_SECRET'),
redirect_uri='http://localhost:8000/auth/callback',
fetch_userinfo=True,
)
),
session=session,
)
app = KayaApp(mixins=[session, oidc])
@oidc.GET('/')
@app.GET('/')
async def home(ctx: HttpContext):
await ctx.send_str(200, 'public home')
@oidc.GET('/profile')
@app.GET('/profile')
@oidc.require_auth
async def profile(ctx: HttpContext):
user = oidc.get_user(ctx)
await ctx.send_str(200, f'Hello {user.email or user.sub}')
```
`OIDCMixin` depends on `SessionMixin`; passing only `oidc` to `KayaApp(mixins=...)`
also works because the app applies mixin dependencies automatically.
## Features
- Generic OIDC discovery
@@ -48,6 +50,7 @@ async def profile(ctx: HttpContext):
- Optional userinfo endpoint fetch
- Refresh token support
- RP-initiated logout (when provider advertises `end_session_endpoint`)
- Composable with any other `KayaMixin` (RSGI, MCP, etc.)
## Security notes