Askr
Routing & Data

Access Policies

Use access policies with the current published Askr packages.

  • @askrjs/askr0.0.59

How to use access policies

Return an explicit allow, redirect, unauthorized, or forbidden decision from the route policy and repeat the enforcement on server APIs.

  1. Import the published @askrjs/askr entrypoint.
  2. Keep access policies configuration next to the component, route, or server composition root that owns it.
  3. Handle pending, unavailable, cancellation, and failure states, then verify the production path.
route('/admin', AdminPage, {
  policy: ({ auth }) =>
    auth.permissions.includes('admin') ? allow() : forbidden(),
});

Policy decisions

A `RoutePolicy` is a function from `RouteContext` to an `AccessDecision`, returned either synchronously or as a promise. There are exactly three decision shapes: `allow()`, `redirect(to, init?)`, and `deny(status)`, and helper functions `unauthorized()`, `forbidden()`, and `notFound()` cover the common deny statuses (401, 403, 404) without you having to remember the numbers. Policies attach to a route, a `page()`, or a whole `group()` via `CommonAccessOptions.policies`, and a group's policies run for every route nested inside it in addition to whatever the specific route adds.

Authentication context

`currentAuth()` returns the `AuthContext` resolved for the route currently rendering, and it's the same object available as `context.auth` inside a policy or loader. Resolving that context is configurable per-app through `RouteAuthOptions.resolve`, a `RouteAuthResolver` that runs before policies and produces the `AuthContext` they'll see — so the actual mechanism (cookie, token, session lookup) lives in one place rather than being re-implemented in every policy. `RegisterRoutesOptions.auth` sets this resolver at the manifest level, and individual routes can still opt into stricter requirements via `CommonAccessOptions.auth`.

Redirects and denials

`redirect(to, init?)` returns an `AccessRedirectDecision` with a configurable HTTP status (301, 302, 303, 307, or 308) and an optional `replace` flag so the redirect doesn't leave the denied URL in browser history. `RouteAuthOptions.loginPath` and `authenticatedRedirectTo` cover the two most common redirect cases directly — where to send an unauthenticated user, and where to send an already-authenticated user who hits a route like a login page — either as a static path or a function of the request context. A `deny()` decision, by contrast, renders in place with the given status rather than sending the browser anywhere.

Server enforcement

Because policies run inside `resolveRouteRequest()` before a `RouteRenderResult` is produced, the same access rules apply whether the request is being resolved for SSR, SSG, or client-side navigation — a policy isn't a client-only convenience that a curious user can bypass by hitting the server route directly. `RouteRequestOptions.request` carries the actual `Request` object through to loaders and policies during SSR, so server-only decisions (cookies, headers, IP) are available exactly where they're evaluated, not bolted on afterward.