Architecture
This page describes how Auris is structured internally and how its components interact. Understanding the architecture helps when debugging integration issues, planning security boundaries, or evaluating Auris for enterprise deployment.
High-Level Overview
Auris is a layered platform. Your application communicates with the Auris API and SDKs. The API layer orchestrates Keycloak for identity operations and adds authorization, security, and developer services on top.
Keycloak handles what it is best at: storing user credentials, managing sessions, federating identity from external providers, and issuing tokens. The Auris API handles everything else: fine-grained authorization, security enforcement, webhooks, audit logging, SCIM provisioning, and the developer-facing API surface.
Tenant Model
Every Auris deployment supports multiple tenants. A tenant is a fully isolated environment with its own:
- User accounts and credentials
- Applications (registered OAuth 2.0 clients)
- Roles, permissions, and authorization policies
- Security configuration (rate limits, MFA policy, IP rules, CAPTCHA)
- Email templates, branding, and custom domains
- Audit logs and webhook endpoints
Under the hood, each tenant maps directly to a Keycloak realm. This means tenant isolation is enforced at the identity store level — users in tenant A cannot authenticate against tenant B’s applications, and their data is never commingled.
The x-tenant header is used on most Auris API calls to identify the active tenant context. SDKs handle this automatically based on the domain (or tenant identifier) you configure at initialization.
The string 'default' is an SDK-level fallback for unscoped local development only. Production
deployments must always pass the real Tenant ID. Using 'default' as a hardcoded tenant can
silently disable security features like attack protection and MFA enforcement.
Three-Layer Authorization
Auris implements authorization in three layers, each adding more granularity than the previous:
Layer 1 — Keycloak (Authentication + Role Mapping)
Keycloak performs authentication and issues tokens containing a user’s realm roles and application-scoped roles. This is the foundational layer — every authenticated session goes through Keycloak.
Keycloak roles are coarse-grained (e.g., admin, user, viewer). They appear in the JWT realm_access and resource_access claims and are used by Keycloak’s own session management and federation features.
Layer 2 — Prisma RBAC (Fine-Grained Roles with Tri-State Permissions)
Auris maintains its own role and permission store in PostgreSQL (via Prisma). This layer provides:
- Roles with a named set of permissions
- Permissions in
action:resourceformat (e.g.,manage:users,view:invoices,approve:expenses) - Tri-state permission values:
ALLOW,DENY, orINHERITALLOW— explicitly grants the permissionDENY— explicitly revokes it, even if another role grants it (DENY wins)INHERIT— falls back to the parent role or tenant default
- Per-user permission overrides that can grant or revoke specific permissions regardless of role
- Application-scoped permissions — the same user can have different permissions in different registered applications
Permission checks happen server-side via POST /api/roles/check. The dashboard enforces permissions on every API route using requirePermission(req, 'action:resource').
Layer 3 — FGA (Fine-Grained Authorization / Zanzibar-style ReBAC)
The FGA layer implements object-level, relationship-based access control, inspired by Google’s Zanzibar paper. It answers questions like “can user Alice read document 42?” or “is user Bob a member of organization X?”.
The FGA model consists of:
- Authorization Model — a schema (written in OpenFGA DSL) defining object types and the relations between them
- Relationship Tuples — facts stored in the database (e.g.,
document:42#viewer@user:alice) - Check engine — a recursive Zanzibar algorithm that evaluates whether a subject has a relation to an object, following computed userset and tuple-to-userset rewrites up to a configurable depth (default: 25)
FGA supports six rewrite rule types: this, computedUserset, tupleToUserset, union, intersection, and exclusion. It also supports expand (list all subjects for a relation) and listObjects (list all objects a subject has access to via reverse lookup).
When FGA_ENGINE_ENABLED=true, resource access checks are routed to the FGA engine. The legacy Ory Keto integration remains as a fallback and is deprecated.
Application Types
Auris supports four application types, corresponding to standard OAuth 2.0 client profiles:
| Type | Description | Auth Flow |
|---|---|---|
WEB | Browser-based apps (SPAs, server-rendered) | Authorization Code + PKCE |
MOBILE | Native iOS / Android apps | Authorization Code + PKCE |
API | Resource servers that validate tokens | Token introspection / JWKS |
M2M | Server-to-server, background jobs, CLI tools | Client Credentials grant |
Each application is assigned a Client ID (public) and optionally a Client Secret (for confidential clients). Redirect URIs are registered per application and enforced exactly — no wildcard matching.
For M2M applications, scopes control what the token allows (e.g., read:users, write:invoices). Custom JWT claims can be attached per application to embed additional data into access tokens without an extra API call.
Token Flow
Auris issues three types of tokens following the OpenID Connect specification:
Access Token
- Format: JWT (JSON Web Token), signed with RS256
- Lifetime: Short (typically 5–60 minutes, configurable per tenant)
- Contains:
sub(user ID),email,roles, custom claims,aud(application),iss(Auris URL),exp - Usage: Sent in the
Authorization: Bearer <token>header to protected API endpoints - Verification: Validate against the JWKS endpoint at
/api/.well-known/jwks.json
Refresh Token
- Format: Opaque (random string, not a JWT)
- Lifetime: Long (typically 7–30 days, configurable)
- Usage: Exchanged for a new access token at
POST /api/auth/token(grant typerefresh_token) without requiring the user to re-authenticate - Security: Single-use rotation — each refresh issues a new refresh token and invalidates the previous one
ID Token
- Format: JWT, signed with RS256
- Contains: User identity claims (
name,email,picture,phone_number,locale, custom attributes) - Usage: Used by the client application to display user information — not sent to APIs
- Verified: Same JWKS endpoint as the access token
OIDC Discovery
Auris publishes a standard OIDC Discovery document at:
GET /api/.well-known/openid-configurationThis endpoint returns the authorization endpoint, token endpoint, JWKS URI, supported grant types, scopes, and signing algorithms. Standard OIDC clients can auto-configure from this document.
The JWKS endpoint:
GET /api/.well-known/jwks.jsonReturns the current public key set in JWK format. Keys use RS256. Key rotation is supported — old keys remain in the JWKS response for a grace period after rotation so in-flight tokens remain valid.
Hosted Login Pages
Auris provides hosted login pages served from the Auris domain (or your custom domain). These pages handle the OAuth 2.0 Authorization Code + PKCE flow end-to-end:
- Your application calls
loginWithRedirect()(SDK) or redirects toGET /api/oauth/authorize - The user sees the Auris-hosted login page, styled with your tenant’s branding (logo, colors, favicon)
- After successful authentication, Auris redirects back to your
redirect_uriwith an authorization code - Your application exchanges the code for tokens at
POST /api/auth/token(handled automatically by SDKs) - The SDK stores the access token and refresh token, and the user is authenticated
All security layers (CAPTCHA, rate limiting, brute-force protection, suspicious login detection, adaptive MFA) are applied during the hosted login flow. You do not need to implement these on your own login pages.
Actions Engine
Actions are custom JavaScript functions that run during authentication flows. They execute in a sandboxed environment with a restricted scope — require, import, process, eval, fs, and child_process are blocked.
Actions can be triggered at six points:
| Trigger | When it fires |
|---|---|
pre_login | Before Keycloak authenticates the user |
post_login | After successful authentication, before token issuance |
pre_signup | Before a new user account is created |
post_signup | After a new user account is created |
post_change_password | After a password change |
pre_m2m_token | Before issuing a machine-to-machine token |
Actions have access to a context object containing the user, tenant, application, and event data. They can deny the auth flow, enrich the token with additional claims, or call external services.
Security Architecture
Security is applied in layers, from network to application:
Rate Limiting
Four rate limit tiers with sliding-window counters:
| Tier | Applied to | Default limit |
|---|---|---|
auth | Login, signup, password reset | 10 requests / 15 minutes |
sensitive | 2FA, magic links, phone verify | 5 requests / hour |
api | Authenticated API calls | 100 requests / minute |
public | Unauthenticated public endpoints | 300 requests / minute |
Rate limit state uses in-memory counters. Standard Retry-After and X-RateLimit-* headers are returned on 429 responses.
Brute Force Protection
Failed login attempts are tracked per account. After a configurable threshold (default: 5 failures in 15 minutes), the account is temporarily locked. Administrators can view and unlock accounts from the console.
CAPTCHA
Three providers are supported: Cloudflare Turnstile, hCaptcha, and Google reCAPTCHA v3. CAPTCHA can be configured to trigger always, only on suspicious requests, or after a number of failed login attempts. CAPTCHA verification is enforced server-side — client-side tokens are validated against the provider’s API before authentication proceeds.
IP Rules
Each tenant can define CIDR-based allow and block rules. Rules can be scoped to the entire tenant or to a specific application. Block rules are evaluated first; if a request IP matches a block rule, it is rejected before any authentication logic runs.
Suspicious Login Detection
After each successful authentication, Auris evaluates five risk signals:
- New device — the device fingerprint has not been seen before for this user
- New IP — the IP address has not been used before for this user
- New country — the geographic country differs from previous sessions
- Impossible travel — the distance between this login and the previous one is too large for the elapsed time
- VPN / proxy / datacenter IP — the IP is identified as a VPN exit node, open proxy, or datacenter address
If any signal triggers, a SuspiciousLoginEvent is recorded and an alert notification is sent. Depending on tenant configuration, suspicious logins can be blocked, challenged with step-up MFA, or allowed with logging only.
DPoP Token Binding
For applications requiring the highest level of token security, Auris supports Demonstrating Proof of Possession (DPoP, RFC 9449). DPoP binds access tokens to a client’s public key, preventing stolen tokens from being used by a different client. The dpop-validator middleware validates DPoP proofs on every request for applications with DPoP enabled.
Data Model Summary
The core entities in Auris and their relationships:
All Prisma model changes (new fields or tables) require npx prisma generate and
npx prisma db push to take effect. Pre-existing model errors in the networking
section resolve automatically after running these commands.