Skip to content

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.

Terminal window
yarn add @stratal/framework

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.

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 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)
}
}
MethodReturn typeDescription
isAuthenticated()booleanWhether the current request has a valid session
getUserId()string | undefinedThe authenticated user’s ID, or undefined
requireUserId()stringThe authenticated user’s ID. Throws if not authenticated
getAuthContext()AuthInfoThe full authentication context object
clearAuthContext()voidClears the authentication state

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.

The AuthModule automatically registers two middleware components:

  1. AuthContextMiddleware — Creates an AuthContext instance in the request-scoped container for every request.
  2. SessionVerificationMiddleware — Verifies the session token from the request and populates AuthContext with the authenticated user’s information.

These middleware run before your controller methods, so AuthContext is always available and populated when your code executes.

  • 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.