Skip to content
GitHub

Auth (lexigram-auth)

Authentication and authorization for the Lexigram Framework — JWT, OAuth2, SAML, RBAC, and multi-tenancy.


Complete authentication and authorization stack for Lexigram — JWT, OAuth2, RBAC, SAML, passkeys, and MFA. Provides a production-ready auth layer with multiple authentication strategies, policy-based access control, session management, and seamless integration with lexigram-web middleware.

Use AuthModule.configure() to register the auth bundle and protect routes with @require_auth, @require_roles, and @require_permissions decorators.

Full documentation: docs.lexigram.dev

Terminal window
uv add lexigram-auth
# Optional extras
uv add "lexigram-auth[oauth2,saml]"
from lexigram import Application
from lexigram.di.module import Module, module
from lexigram.auth import AuthModule, AuthConfig, JWTConfig
@module(imports=[
AuthModule.configure(
config=AuthConfig(
secret_key="your-secret-key",
token=JWTConfig(secret_key="your-jwt-secret"),
)
)
])
class AppModule(Module):
pass
async def main() -> None:
async with Application.boot(modules=[AppModule]) as app:
# app is running — resolve services from app.container
...
if __name__ == "__main__":
import asyncio
asyncio.run(main())

Note: AuthConfig requires both secret_key and token.secret_key — pass an explicit config via AuthModule.configure().

application.yaml
auth:
secret_key: "your-secret-key"
token:
secret_key: "your-jwt-secret"
algorithm: "HS256"
access_token_expire: "30m"
rbac:
enabled: true
default_role: "viewer"
Section titled “Option 2 — Profiles + Environment Variables (recommended)”
Terminal window
export LEX_AUTH__SECRET_KEY=your-secret-key
export LEX_AUTH__TOKEN__SECRET_KEY=your-jwt-secret
export LEX_AUTH__TOKEN__ALGORITHM=HS256
export LEX_AUTH__RBAC__DEFAULT_ROLE=viewer
from lexigram.auth import AuthModule, AuthConfig, JWTConfig
from lexigram.contracts.core import Duration
config = AuthConfig(
secret_key="your-secret-key",
token=JWTConfig(
secret_key="your-jwt-secret",
algorithm="HS256",
access_token_expire=Duration.minutes(30),
),
)
AuthModule.configure(config)
FieldDefaultEnv varDescription
secret_keyLEX_AUTH__SECRET_KEYTop-level signing secret (required)
token.secret_keyLEX_AUTH__TOKEN__SECRET_KEYJWT signing secret (required)
token.algorithmHS256LEX_AUTH__TOKEN__ALGORITHMJWT algorithm: HS256, RS256, ES256
token.access_token_expire30mLEX_AUTH__TOKEN__ACCESS_TOKEN_EXPIREAccess token lifetime (duration string, e.g. 30m, 1h30m)
rbac.enabledTrueLEX_AUTH__RBAC__ENABLEDEnable RBAC
rbac.default_roleviewerLEX_AUTH__RBAC__DEFAULT_ROLEDefault role for new users
MethodDescription
AuthModule.configure(...)Configure with explicit AuthConfig
AuthModule.stub()Minimal config for testing
  • JWT authentication — HS256/RS256, key rotation, token blacklisting
  • OAuth2 / OIDC — authlib-backed: Google, GitHub, custom providers
  • SAML 2.0 — Enterprise SSO via pysaml2
  • Passkeys (WebAuthn) — FIDO2 device-based authentication
  • MFA (TOTP) — Time-based one-time passwords
  • RBAC — Role/permission inheritance with policy expressions
  • Session management — Device-aware sessions with concurrency limits
  • Token binding — IP address binding to prevent token theft
async with Application.boot(modules=[AuthModule.stub()]) as app:
# your test code
...
FileWhat it contains
src/lexigram/auth/module.pyAuthModule definition
src/lexigram/auth/config.pyAuthConfig, JWTConfig (+ allow_unverified_dev), RBACConfig
src/lexigram/auth/di/bundle_provider.pyAuthBundleProvider wiring
src/lexigram/auth/di/sub_providers/token_provider.pyTokenProvider (boots policy)
src/lexigram/auth/authn/jwt.pyJWTTokenManager implementation
src/lexigram/auth/authn/_jwt_lifecycle.pyverify_token (enforces policy)
src/lexigram/auth/authz/service.pyAuthorizationService

lexigram-auth enforces verified-only JWT decoding by default.

EnvironmentSecret presentallow_unverified_devBehaviour
PRODUCTION / STAGINGyesanyVerified-only. Boot succeeds.
PRODUCTION / STAGINGnoanyRaises ConfigurationError at boot. Flag ignored.
DEVELOPMENTyesanyVerified-only. Boot succeeds.
DEVELOPMENTnoFalse (default)Raises ConfigurationError at boot.
DEVELOPMENTnoTrueBoots. Single warning logged. Tokens decoded without signature verification.

Via environment variable:

Terminal window
export LEX_AUTH__TOKEN__ALLOW_UNVERIFIED_DEV=true

Via Python config:

from lexigram.auth.config import AuthConfig, JWTConfig
config = AuthConfig(
secret_key="any-placeholder",
token=JWTConfig(
secret_key="any-placeholder",
allow_unverified_dev=True,
),
)

The allow_unverified_dev flag is silently ignored in PRODUCTION and STAGING; the service always rejects the flag in those environments and raises if no real secret is configured. This prevents the Piccolina-style mistake of silently trusting unverified tokens in production when a secret env-var is missing.