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 user = this.auth.requireUser()
return this.usersRepository.findById(user.id)
}
}
MethodReturn typeDescription
isAuthenticated()booleanWhether the current request has a valid session
getUser()AuthUser | undefinedThe authenticated user, or undefined
requireUser()AuthUserThe authenticated user. Throws UserNotAuthenticatedError if not authenticated
getUserId()string | undefinedThe authenticated user’s ID, or undefined
requireUserId()stringThe authenticated user’s ID. Throws if not authenticated
getRole()string | undefinedRaw role string from user.role (comma-separated for multiple roles)
getRoles()string[]Roles parsed into an array. Returns [] when the user has no role
getAuthInfo()AuthInfoThe full authentication context object. Throws ContextNotInitializedError if not initialised
clearAuthContext()voidClears the authentication state

AuthUser extends Better Auth’s base user with name made optional. Augment it via TypeScript module declaration to match whatever additionalFields or plugins your Better Auth config returns:

declare module '@stratal/framework/context' {
interface AuthUser {
firstName: string
lastName: string
role: string
}
}

Once augmented, auth.requireUser() and auth.getUser() return the typed shape, and auth.getRole() is typed as string.

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.
  • Access Control for role-based permissions on top of auth.
  • Database for configuring the database used by Better Auth.