Namespace per-provider session state as oidc:{provider}:... so several providers can share one session, and add an OIDCProviders mixin that wires multiple configs onto distinct /auth/{provider}/... routes. Logging out of one provider no longer signs the user out of the others.
kaya-oidc
OpenID Connect authentication for the Kaya web framework.
Built on top of kaya-session and implements the OIDC Authorization Code
Flow with PKCE.
Usage
import os
from kaya.core import HttpContext, KayaApp
from kaya.session import SessionMixin, InMemorySessionStore
from kaya.oidc import OIDCConfig, OIDCMixin
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])
@app.GET('/')
async def home(ctx: HttpContext):
await ctx.send_str(200, 'public home')
@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.
Multiple providers
Use OIDCProviders to run several OIDC providers in one application. Each
config's provider becomes a route segment under base_path
(/auth/{provider}/login, /auth/{provider}/callback and
/auth/{provider}/logout), and all providers share a single SessionMixin:
from kaya.oidc import OIDCConfig, OIDCProviders
session = SessionMixin(InMemorySessionStore())
oidc = OIDCProviders(
[
OIDCConfig(
issuer='https://accounts.google.com',
client_id=os.environ['GOOGLE_CLIENT_ID'],
client_secret=os.environ['GOOGLE_CLIENT_SECRET'],
redirect_uri='http://localhost:8000/auth/google/callback',
provider='google',
),
OIDCConfig(
issuer='https://github.com/login/oauth', # or any OIDC issuer
client_id=os.environ['GITHUB_CLIENT_ID'],
client_secret=os.environ['GITHUB_CLIENT_SECRET'],
redirect_uri='http://localhost:8000/auth/github/callback',
provider='github',
),
],
session=session,
base_path='/auth', # optional
)
app = KayaApp(mixins=[session, oidc])
@app.GET('/google-profile')
@oidc.require_auth('google')
async def google_profile(ctx: HttpContext):
user = oidc.get_user(ctx, 'google')
await ctx.send_str(200, f"Hello {user.email or user.sub}")
@app.GET('/who')
async def who(ctx: HttpContext):
users = oidc.get_users(ctx) # {provider: OIDCUser}
providers = oidc.logged_in_providers(ctx) # ['google', ...]
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.
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:
google = OIDCMixin(google_config, session=session)
github = OIDCMixin(github_config, session=session)
app = KayaApp(mixins=[session, google, github])
Upgrading: session keys are now prefixed with
oidc:{provider}:(the default provider isdefault). Existing logged-in users are signed out once and simply re-authenticate.
Features
- Generic OIDC discovery
- Authorization Code Flow with PKCE (S256)
- ID token signature validation with JWKS
stateandnonceprotection- Session fixation defense via
regenerate_id()after login - Optional userinfo endpoint fetch
- Refresh token support
- RP-initiated logout (when provider advertises
end_session_endpoint) - Multiple providers in one app via
OIDCProviders(or severalOIDCMixininstances) - Composable with any other
KayaMixin(RSGI, MCP, etc.)
Security notes
- The
nonesigning algorithm is rejected by default. - Only algorithms listed in
OIDCConfig.allowed_id_token_algorithmsare accepted. - Always use HTTPS for
redirect_uriin production.
Supported flows
Only the Authorization Code Flow with PKCE is supported. Implicit and Hybrid flows are intentionally not implemented.
Supported algorithms
ID token signature verification supports:
RS256, RS384, RS512, PS256, PS384, PS512,
ES256, ES384, ES512, and EdDSA.
HMAC algorithms (HS*) are disabled by default and can be enabled by adding
them to allowed_id_token_algorithms if your provider uses them.