Authentication Setup¶
Mozaiks uses a pluggable auth adapter system that works with any auth provider.
Quick Start¶
Demo Mode (No Auth)¶
For local development or demos, disable auth entirely:
All requests become anonymous users. No tokens required.
For local user-to-user testing, such as DM notifications, keep auth disabled but assign each browser profile a different dev persona. The no-auth dependency accepts these request-scoped overrides only when AUTH_ENABLED=false:
- Header:
X-Mozaiks-Dev-User-Id: dev_alice - Cookie:
mozaiks_dev_user_id=dev_alice - Query param for one-off API calls:
?dev_user_id=dev_alice
Optional comma-separated overrides are also supported:
X-Mozaiks-Dev-Roles,mozaiks_dev_roles, ordev_rolesX-Mozaiks-Dev-Scopes,mozaiks_dev_scopes, ordev_scopes
Browser workflow:
document.cookie = "mozaiks_dev_user_id=dev_alice; path=/";
document.cookie = "mozaiks_dev_roles=admin,user; path=/";
Use another browser profile or incognito window with mozaiks_dev_user_id=dev_bob. Sending a DM from dev_alice to dev_bob can create a notification for Bob; sending to yourself will not, because the messaging module excludes the sender from recipient_ids.
Provider Setup¶
Supabase¶
That's it. The adapter auto-detects from SUPABASE_URL and constructs the JWKS endpoint automatically.
Optional: For local development with Supabase, add the JWT secret:
Keycloak¶
KEYCLOAK_URL=https://keycloak.example.com
KEYCLOAK_REALM=myrealm
KEYCLOAK_CLIENT_ID=my-app # optional, for audience validation
Auth0¶
AUTH_PROVIDER=jwt
MOZAIKS_OIDC_AUTHORITY=https://your-tenant.auth0.com
AUTH_AUDIENCE=your-api-identifier
AUTH_SCOPES_FORMAT=array
Set AUTH_JWKS_URL and AUTH_ISSUER only when you want to override OIDC discovery explicitly.
Generic OIDC (Okta, Azure AD, etc.)¶
For providers that require a tenant segment in the discovery URL, set:
You can also bypass authority composition with:
Explicit overrides remain supported:
Claim Mappings¶
Different providers put user data in different JWT claims. Configure as needed:
| Variable | Default | Description |
|---|---|---|
AUTH_USER_ID_CLAIM | sub | Claim containing user ID |
AUTH_EMAIL_CLAIM | email | Claim containing email |
AUTH_NAME_CLAIM | name | Claim containing display name |
AUTH_ROLES_CLAIM | roles | Claim containing user roles |
AUTH_SCOPES_CLAIM | scp | Claim containing scopes |
AUTH_SCOPES_FORMAT | space | space (Azure) or array (Auth0) |
Auto-Detection¶
If AUTH_PROVIDER is not set, the system auto-detects based on environment variables:
| If this is set... | Provider used |
|---|---|
AUTH_ENABLED=false | none |
SUPABASE_URL | supabase |
KEYCLOAK_URL + KEYCLOAK_REALM | keycloak |
AUTH_JWKS_URL + AUTH_ISSUER | jwt |
MOZAIKS_OIDC_AUTHORITY or MOZAIKS_OIDC_DISCOVERY_URL | jwt |
| Nothing | none (demo mode) |
Custom Adapter¶
Register your own auth provider:
from mozaiksai.core.auth.adapters import register_adapter, UserClaims, BaseAuthAdapter
class MyAuthAdapter(BaseAuthAdapter):
name = "my-provider"
async def validate_token(self, token: str) -> UserClaims:
# Your validation logic
decoded = my_validate(token)
return UserClaims(
user_id=decoded["sub"],
email=decoded.get("email"),
roles=decoded.get("roles", []),
scopes=[],
raw_claims=decoded,
provider=self.name,
)
def is_enabled(self) -> bool:
return bool(os.getenv("MY_AUTH_SECRET"))
# Register before app startup
register_adapter("my-provider", MyAuthAdapter)
Then set:
WebSocket Auth¶
WebSocket connections extract tokens from query params by default:
To disable (in production behind reverse proxy):