Skip to main content

Gates

Gates are functions that determine whether a session is allowed to perform an action. They return true, false, or a GateResult with an explicit error code.

Gate Type

type Gate = (session: Session | null) => Promise<GateResultType> | GateResultType
type GateResultType = GateResult | boolean

A gate receives the current session (possibly null) and returns:

  • true -- access granted
  • false -- access denied (defaults to 403)
  • GateResult -- access denied with a specific error code (401 or 403)

GateResult

import { createGateResult } from '@repo/auth'

// Success
createGateResult(true)

// Failure with error code
createGateResult(false, 401) // unauthenticated
createGateResult(false, 403) // unauthorized

The GateResult class has two readonly properties: success (boolean) and errorCode (401 | 403 | undefined).

Creating Custom Gates

import type { Gate } from '@repo/auth'
import { createGateResult } from '@repo/auth'

export function createAuthorityGate(authorities: string[]): Gate {
return (session) => {
if (!session?.user) {
return createGateResult(false, 401)
}

const hasAll = authorities.every(
(val) => session.user.authorities.includes(val)
)

if (!hasAll) {
return createGateResult(false, 403)
}

return true
}
}

Built-in Gates

isAuthenticatedGate

Returns createGateResult(false, 401) when session.user.id is missing. Otherwise returns true.

import { isAuthenticatedGate } from '@repo/auth'

permissionGate

Factory that creates a gate checking domain + action permissions on the session entity.

import { permissionGate } from '@repo/auth'

const gate = permissionGate('invoices', ['read', 'update'])

Uses hasPermission internally against session.entity.permissions.

hasAccess

Evaluates multiple gates concurrently. Returns a GateResult reflecting the most restrictive failure (401 takes precedence over 403).

import { hasAccess } from '@repo/auth'

const result = await hasAccess(session, [isAuthenticatedGate, permissionGate('users', ['delete'])])
// result.success -- boolean
// result.errorCode -- 401 | 403 | undefined

Server-side hasAccess

The server re-export automatically reads the session from the current request context:

import { hasAccess } from '@repo/auth/server'

const result = await hasAccess([isAuthenticatedGate])

Client-side hasAccess

Checks gates against page.data.session and resolves to a boolean:

import { hasAccess } from '@repo/auth/client'

const allowed = await hasAccess([permissionGate('users', ['read'])])
// allowed: boolean