Authentication
Introduction
Section titled “Introduction”Authentication answers a fundamental security question:
Who is making this request?
In Xeno.JS, authentication takes a credential from the incoming request, validates it through an authentication service, and establishes the authenticated identity used by the rest of the application.
The authentication flow is:
HTTP credential │ ▼Token extractor │ ▼Authentication middleware │ ▼Authentication service │ ▼Auth claims │ ▼Request identity │ ▼Application executionAuthentication establishes identity. It does not decide whether that identity is allowed to perform a particular application operation.
Authorization is responsible for that decision.
Add Authentication
Section titled “Add Authentication”Authentication is configured through AppBuilder.
A basic application configuration looks like this:
const app = new AppBuilder()
app.addAuth((options, config) => { options.url = config.get('SUPABASE_URL')! options.key = config.get('SUPABASE_KEY')!})
app.addMiddlewares()
const container = await app.build()There are two important pieces here:
addAuth()configures the authentication service.addMiddlewares()enables the HTTP middleware that extracts the request credential and authenticates the request.
Without the authentication middleware, configuring an authentication service does not by itself authenticate incoming HTTP requests.
How Authentication Works
Section titled “How Authentication Works”Xeno.JS separates two responsibilities that are often combined in application code.
1. Credential extraction
Section titled “1. Credential extraction”The token extractor answers:
Where does the credential come from?
For example, an HTTP authentication credential may come from:
Authorization: Bearer <token>or from an authentication cookie.
The extractor is responsible for retrieving the credential from the transport.
It does not decide whether the credential is valid.
2. Credential validation
Section titled “2. Credential validation”The authentication service answers:
Is this credential valid, and which identity does it represent?
The authentication service validates the credential and returns authentication claims.
Conceptually:
Request │ ├── extractor ──────► credential │ └── auth service ◄─── credential │ ▼ AuthClaimsKeeping these responsibilities separate allows the same authentication model to work with different credential formats and authentication providers.
Authentication Claims
Section titled “Authentication Claims”A successful authentication produces claims describing the authenticated identity.
The claims can contain information such as:
{ sub: 'user-id', email: 'user@example.com', name: 'Jane Doe', tenantId: 'tenant-id', roles: ['admin'], permissions: ['orders:read']}The exact claims depend on the authentication provider.
Xeno.JS then maps the authentication claims to the request identity used by the application.
This identity is what authorization strategies consume later in the request lifecycle.
Authentication │ ▼Authentication claims │ ▼Request identity │ ▼AuthorizationConfigure Authentication with Supabase
Section titled “Configure Authentication with Supabase”Xeno.JS provides a built-in Supabase authentication integration.
The minimal configuration is:
const app = new AppBuilder()
app.addAuth((options, config) => { options.url = config.get('SUPABASE_URL')! options.key = config.get('SUPABASE_KEY')!})
app.addMiddlewares()
const container = await app.build()The authentication options include:
interface AuthConfig<TOptions> { url: string key: string opts?: TOptions storageOpts?: { type?: 'local' | 'session' | 'memory' | 'cookie' cookieOpts?: CookieOptions storage?: IStorage } redirectTo?: string}These options configure the authentication provider and how authentication state is stored.
For example:
urlidentifies the authentication service.keyprovides the client key required by the provider.optsprovides provider-specific options.storageOptscontrols the authentication state storage strategy.redirectToconfigures the application redirect destination where supported.
For Supabase-specific configuration, including SSR cookies, session handling, and authentication cookies, see:
Use a Custom Authentication Service
Section titled “Use a Custom Authentication Service”You do not have to use the built-in Supabase integration.
Xeno.JS supports a custom authentication service when your application uses another identity provider or has its own authentication mechanism.
Configure it through customAuth:
const app = new AppBuilder()
app.addAuth((options) => { options.customAuth = { authHeaderExtractor: () => new MyTokenExtractor(), authExtendedService: () => new MyAuthService(), }})
app.addMiddlewares()
const container = await app.build()The two components have different responsibilities:
MyTokenExtractor │ └── extracts the credential
MyAuthService │ └── validates the credential and returns authentication claimsThe custom authentication service implements the Xeno.JS authentication contract.
At its base, the authentication service provides operations such as:
interface IBaseAuthService { isAuthenticated(): Promise<boolean>
getUser(): Promise<ResultType<Maybe<AuthClaims>>>
authenticate(token: string): Promise<ResultType<AuthClaims>>}This allows Xeno.JS to work with authentication systems other than Supabase without changing the application authentication flow.
For the complete custom provider implementation, see:
Authentication Middleware
Section titled “Authentication Middleware”Authentication is applied at the HTTP boundary by Xeno.JS middleware.
The resulting flow is:
HTTP request │ ▼Credential extraction │ ▼Authentication │ ▼Authenticated identity │ ▼Request context │ ▼Application executionWhen authentication succeeds, the authenticated identity becomes available to the request context.
When authentication fails, the request does not continue as an authenticated request.
This makes authentication available to the application without requiring individual commands or services to manually parse HTTP headers or authentication cookies.
Authentication and Authorization
Section titled “Authentication and Authorization”Authentication and authorization solve different problems.
Authentication
Section titled “Authentication”Authentication answers:
Who are you?
credential ↓authenticated identityAuthorization
Section titled “Authorization”Authorization answers:
Are you allowed to perform this operation?
authenticated identity ↓authorization policy ↓allowed / deniedFor example, authentication may establish:
{ sub: 'user-123', tenantId: 'tenant-456', roles: ['operator']}Authorization can then decide whether that identity may execute a specific command.
Do not put application authorization rules into the authentication service.
Authentication establishes identity. Authorization evaluates access.
See Authorization for application-level access control.
Authentication and the Request Context
Section titled “Authentication and the Request Context”Once authentication succeeds, the resulting identity is associated with the current request context.
This allows application code to work with the authenticated identity without depending directly on the HTTP transport.
The architectural flow is therefore:
HTTP │ │ authentication ▼Request Context │ │ identity ▼Application │ │ authorization ▼Use CaseThis is particularly useful for CQRS handlers and application services because they can consume identity information without directly depending on Request, Response, or HTTP headers.
When Should You Use Custom Authentication?
Section titled “When Should You Use Custom Authentication?”Use the built-in Supabase integration when Supabase is responsible for authenticating your application users.
Use a custom authentication service when your application needs to integrate with another authentication provider or an existing authentication system.
Typical examples include:
- an enterprise identity provider;
- an internal authentication service;
- a custom JWT issuer;
- an API gateway that provides application credentials;
- an existing authentication system that cannot be replaced.
The important requirement is that the custom implementation can turn the incoming credential into the authentication claims required by the application.
Authentication Is Not Authorization
Section titled “Authentication Is Not Authorization”Do not assume that an authenticated request is automatically authorized to perform every operation.
For example, an authenticated identity might contain:
{ sub: 'user-123', tenantId: 'tenant-456', roles: ['user']}This only establishes who the caller is.
An application can still require:
role = adminor:
permission = orders:createor a more specific business rule such as:
the authenticated user owns this orderThose checks belong to authorization.
Xeno.JS provides built-in authorization strategies for common identity and access checks and allows custom authorization strategies for application-specific rules.
Recommended Authentication Configuration
Section titled “Recommended Authentication Configuration”For a typical HTTP application, configure authentication together with the middleware layer:
const app = new AppBuilder()
app.addAuth((options, config) => { options.url = config.get('SUPABASE_URL')! options.key = config.get('SUPABASE_KEY')!})
app.addMiddlewares()
app.addPipeline()
const container = await app.build()This gives the application the following security flow:
HTTP request │ ▼Authentication middleware │ ▼Authenticated identity │ ▼Application pipeline │ ▼Authorization │ ▼Command / Query handlerThe exact middleware configuration depends on the application’s transport and security requirements. Authentication can be combined with CSRF protection, CORS controls, allowed HTTP methods, and rate limiting.
See the HTTP Security Middleware documentation for those controls.
Common Configuration Mistakes
Section titled “Common Configuration Mistakes”Authentication is configured but requests are not authenticated
Section titled “Authentication is configured but requests are not authenticated”Make sure the middleware layer is enabled:
app.addAuth((options) => { // authentication configuration})
app.addMiddlewares()addAuth() configures the authentication service; addMiddlewares() enables the HTTP middleware that uses it.
The application parses the Authorization header inside handlers
Section titled “The application parses the Authorization header inside handlers”Do not make application handlers responsible for extracting HTTP credentials.
Keep transport-specific credential extraction in the authentication middleware and expose the resulting identity through the request context.
This keeps the application layer independent from HTTP.
Authentication logic contains business authorization rules
Section titled “Authentication logic contains business authorization rules”Avoid implementing rules such as:
is this user an administrator?does this user own this resource?does this user belong to this tenant?inside the authentication service.
The authentication service should establish the identity.
Use authorization policies and custom authorization strategies for access decisions.
A custom provider only implements token extraction
Section titled “A custom provider only implements token extraction”A token extractor only retrieves the credential.
It does not validate the credential or establish the authenticated identity.
A custom authentication integration normally requires both:
credential extractor +authentication serviceNext Steps
Section titled “Next Steps”Once authentication is configured, continue with the provider-specific or application-specific integration:
- Supabase Authentication — configure the built-in Supabase integration, cookies, SSR, and token extraction.
- Custom Authentication — implement your own authentication service and credential extractor.
- Authorization — use the authenticated identity to control access to application operations.
- HTTP Security Middleware — configure authentication together with CSRF, CORS, method restrictions, and rate limiting.
Support Us
Section titled “Support Us”Xeno.JS is an MIT-licensed open source project. It can grow thanks to the support of these awesome people. If you’d like to join them, please read more at support section
