---
title: "@playstack/nest-auth"
description: "NestJS guards and request decorators for Playstack sessions and CSRF transport."
tags: ["package","identity","auth","nestjs","free"]
---

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/nest-auth` connects `@playstack/auth` session validation and its fixed CSRF transport to NestJS guards and decorators.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/nest-auth@0.1.0-beta.1
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Install and compose

```ts
PlaystackAuthModule.forRoot({
  sessions,
  allowedOrigins: ['https://app.example'],
})
```

Use `forRootAsync({ imports, inject, useFactory })` when the session strategy depends on other Nest providers.

```ts
@UseGuards(PlaystackAuthGuard, PlaystackCsrfGuard)
@Post('/profile')
update(@CurrentSession() session: SessionClaims) {}

@Public()
@Get('/health')
health() {}
```

## Configuration reference

| `NestAuthOptions` property | Required | Purpose |
| --- | --- | --- |
| `sessions` | Yes | Any structural `SessionStrategy`, or a factory `({ devices }) => SessionStrategy` that receives the injected device validator. |
| `allowedOrigins` | Yes | Exact origins accepted for cookie-authenticated unsafe requests. |
| `devices` | No | A `DeviceSessionValidator` such as the `@playstack/devices` service; also resolvable from the `NEST_DEVICE_SESSION_VALIDATOR` provider. |
| `guards` | No | The composition `PlaystackGuardStack` runs: `'cookie'` (default), `'bearer'`, or guard classes in order. |
| `globalGuards` | No | Registration only: install the composed stack as a global guard; routes opt out with `@Public()`. |
| `imports` | No | `forRoot` only: modules exporting `NEST_DEVICE_SESSION_VALIDATOR` or application guards. |
| `sessionCookieName` | No | Host-only cookie name; must begin `__Host-`. |
| `csrfCookieName` | No | Synchronizer cookie name; must begin `__Host-`. |
| `csrfHeaderName` | No | Header carrying the synchronizer token. |

`PlaystackAuthGuard` accepts the host-only cookie or a strict bearer token and attaches only `SessionClaims` as `request.playstackSession`. A validation failure thrown by the strategy, such as a revoked device, is answered with 401; retryable persistence failures keep their original error. `PlaystackCsrfGuard` validates Origin plus synchronizer token for cookie-authenticated unsafe methods; bearer requests do not use ambient browser credentials and bypass CSRF.

## Split-origin cookie routes

When the page and the API are different origins, the page cannot read a CSRF cookie set on the API origin, so the synchronizer-token guards do not apply. `PlaystackOriginGuard` is the defence for that profile: an unsafe browser request must present an `Origin` from `allowedOrigins` and, when the browser sends it, a `Sec-Fetch-Site` that is not `cross-site`. A request with no `Origin`, no Fetch Metadata and no cookie carries no ambient credential and passes, so a CLI, a mobile app or a server can call the same routes with a bearer token or explicit credentials. `isBrowserRequest(request)` exposes that decision for controllers that choose a transport per client. A refusal answers 403 with `{ error: 'origin_not_allowed' }`.

| Guard | Profile |
| --- | --- |
| `PlaystackCsrfGuard` | Same-origin cookie sessions with a synchronizer token; bearer requests are exempt. |
| `PlaystackCookieCsrfGuard` | Cookie-mutating routes on that profile; never bearer-exempt. |
| `PlaystackOriginGuard` | Split-origin bearer contract; browser requests need an allowed `Origin`, non-browser requests pass. |

## Registered devices via dependency injection

`@playstack/devices` is not a dependency of this package. Its service satisfies the `DeviceSessionValidator` contract from `@playstack/auth`, so the module accepts it through Nest injection and passes it to a session factory.

```ts
import { Module } from '@nestjs/common'
import { databaseSessions } from '@playstack/auth'
import { NEST_DEVICE_SESSION_VALIDATOR, PlaystackAuthModule } from '@playstack/nest-auth'

@Module({
  providers: [{ provide: NEST_DEVICE_SESSION_VALIDATOR, useValue: devices }],
  exports: [NEST_DEVICE_SESSION_VALIDATOR],
})
class DevicesModule {}

PlaystackAuthModule.forRoot({
  imports: [DevicesModule],
  sessions: ({ devices }) =>
    databaseSessions({ persistence, crypto, events, clock, ids, deviceValidator: devices, requireDevice: true }),
  allowedOrigins: ['https://app.example'],
})
```

With `forRootAsync`, inject the service and return it as `devices`, or export the provider from an imported module; either way the `sessions` factory receives it and `NEST_AUTH_OPTIONS` exposes the resolved validator. Register `sessions.deviceRevocationHandler()` for `devices.device.revoked` on the shared emitter so revoking a device revokes its sessions in the same transaction.

## Composing guards

The application composes the guards; the package never installs one on its own. Name a default composition by transport profile and place `PlaystackGuardStack` where it belongs, install that composition globally with `globalGuards: true` and opt routes out with `@Public()`, or list your own layering of package guards, other Playstack bindings and application guards.

```ts
PlaystackAuthModule.forRoot({ sessions, allowedOrigins, guards: 'bearer' })

@UseGuards(PlaystackGuardStack)
@Controller('accounts')
export class AccountsController {}

// Your own order, mixing bindings and application guards:
PlaystackAuthModule.forRoot({
  sessions,
  allowedOrigins,
  guards: [PlaystackOriginGuard, TenantGuard, PlaystackAuthGuard, PlaystackAccountGuard],
})
```

`'cookie'` is `PlaystackCsrfGuard` then `PlaystackAuthGuard`; `'bearer'` is `PlaystackOriginGuard` then `PlaystackAuthGuard`; `playstackGuardStacks` exports both for spreading into `@UseGuards`. The stack resolves each guard once from the application's providers, or constructs it with its dependencies when it is not registered, evaluates them in order, and stops at the first denial. A bad composition fails at bootstrap. Every guard stays exported for individual placement.

## Boundary

The underlying `SessionStrategy` is the replacement seam. The adapter does not own users, accounts, provider normalization, persistence, or authorization, and it never installs a guard the application did not ask for. Applications control guard placement and ordering.

## Authorization and credentials are separate surfaces

The package includes optional device-authorization and credential-route integration resources in addition to session/refresh/sign-out composition. Templates are application-owned wiring: configure registration policy, input validation, consent, CSRF/raw transport behavior, rate limits and required transactional adapters before enabling routes. Device and authorization-code success responses are not replayable recovery stores.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/nest-auth@0.1.0-beta.1`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/nest-auth` | `./dist/index.d.ts` |
| `@playstack/nest-auth/device` | `./dist/device.d.ts` |
| `@playstack/nest-auth/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@nestjs/common` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@nestjs/core` | `^10.0.0 \|\| ^11.0.0` | Required by the package. |
| `@playstack/auth` | `0.1.0-beta.1` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.1` | Required by the package. |
| `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` | Required by the package. |
| `rxjs` | `^7.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}
