Auth
@stratal/framework integrates with Better Auth to provide authentication, session management, and a request-scoped AuthContext for accessing the authenticated user throughout your application.
Install dependencies
Section titled “Install dependencies”yarn add @stratal/frameworkConfigure AuthModule
Section titled “Configure AuthModule”Use AuthModule.forRootAsync() to configure authentication with async options:
import { Module } from 'stratal/module'import { DI_TOKENS } from 'stratal/di'import { CONFIG_TOKENS, type ConfigService } from 'stratal/config'import { AuthModule } from '@stratal/framework/auth'
@Module({ imports: [ AuthModule.forRootAsync({ inject: [DI_TOKENS.Database, CONFIG_TOKENS.ConfigService], useFactory: (db, config: ConfigService) => ({ database: db, secret: config.get('AUTH_SECRET'), baseURL: config.get('AUTH_BASE_URL'), // ... other Better Auth options }), }), ],})export class AppModule {}The factory receives resolved dependencies and returns a Better Auth options object. Refer to the Better Auth documentation for all available options.
Auth controller
Section titled “Auth controller”Create a controller to proxy all authentication requests to Better Auth’s handler:
import { Controller, type IController, type RouterContext } from 'stratal/router'import { inject } from 'stratal/di'import { AUTH_SERVICE, type AuthService } from '@stratal/framework/auth'
@Controller('/api/auth')export class AuthController implements IController { constructor(@inject(AUTH_SERVICE) private readonly authService: AuthService) {}
async handle(ctx: RouterContext) { return this.authService.auth.handler(ctx.c.req.raw) }}This controller catches all requests under /api/auth/* and forwards them to Better Auth, which handles sign-up, sign-in, session management, and all other auth endpoints.
AuthContext
Section titled “AuthContext”AuthContext is a request-scoped service that holds the authenticated user’s information for the current request. Inject it using DI_TOKENS.AuthContext:
import { Transient, inject, DI_TOKENS } from 'stratal/di'import { AuthContext } from '@stratal/framework/context'
@Transient()export class ProfileService { constructor( @inject(DI_TOKENS.AuthContext) private readonly auth: AuthContext, ) {}
async getProfile() { const userId = this.auth.requireUserId() return this.usersRepository.findById(userId) }}AuthContext API
Section titled “AuthContext API”| Method | Return type | Description |
|---|---|---|
isAuthenticated() | boolean | Whether the current request has a valid session |
getUserId() | string | undefined | The authenticated user’s ID, or undefined |
requireUserId() | string | The authenticated user’s ID. Throws if not authenticated |
getAuthContext() | AuthInfo | The full authentication context object |
clearAuthContext() | void | Clears the authentication state |
AuthService
Section titled “AuthService”The AuthService provides access to the underlying Better Auth instance for advanced operations like session management:
import { Transient, inject } from 'stratal/di'import { AUTH_SERVICE, AuthService } from '@stratal/framework/auth'
@Transient()export class SessionService { constructor( @inject(AUTH_SERVICE) private readonly authService: AuthService, ) {}
get auth() { return this.authService.auth }}The auth property returns the configured Better Auth instance with full access to its API.
Middleware pipeline
Section titled “Middleware pipeline”The AuthModule automatically registers two middleware components:
- AuthContextMiddleware — Creates an
AuthContextinstance in the request-scoped container for every request. - SessionVerificationMiddleware — Verifies the session token from the request and populates
AuthContextwith the authenticated user’s information.
These middleware run before your controller methods, so AuthContext is always available and populated when your code executes.
Next steps
Section titled “Next steps”- Auth Guard for protecting routes with authentication checks.
- RBAC for role-based access control on top of auth.
- Database for configuring the database used by Better Auth.