# Authentication

> Authentication at the Askr server boundary, with validated input and explicit failure states.

Source: [https://askrjs.com/docs/authentication](https://askrjs.com/docs/authentication)

Status: stable. Packages: @askrjs/auth, @askrjs/server/auth.

**Published packages are authoritative.** Examples may lag behind a published contract. When guidance differs, verify the exports and TypeScript declarations in your installed package, then file an issue.

## Example

Resolve identity at the request boundary and express authorization as reusable requirements instead of component-only checks.

```tsx
import {
  createAuth,
  requirePermission,
  requireUser,
} from '@askrjs/auth';

export const auth = createAuth({
  sessions: sessionStore,
  principals: principalStore,
});

export const manageProjects = [
  requireUser(),
  requirePermission('projects:write'),
];
```

## Identity model

A Principal (id, subject?, roles?, permissions?) and an AuthSession (id, subject, expiresAt?, revokedAt?) are separate types in @askrjs/auth, both extending a Claim base of Record&lt;string, unknown&gt; so you can attach whatever custom fields your provider sends. createAuth(options) builds an AuthResolver whose resolve(request) returns one AuthContext per request, bundling authenticated, principal, session, tenant, and an optional scopes list into a single object. Route handlers and requirement functions both read from that same AuthContext, so there's one shape to reason about regardless of how the principal was established.

## Session boundary

AuthOptions accepts a sessions store, a principals store, a jwt validator, a jwtCookie {name, validator} pair, a tenant resolver, a sessionCookie name, and a clock function - you wire in whichever combination matches your app. During resolve(), createAuth picks whichever configured source actually matches the incoming request (a session cookie, a bearer JWT, a JWT cookie) and normalizes the result to the same AuthContext shape either way. Nothing about session storage lives in @askrjs/server itself; it's entirely delegated to the SessionStore and PrincipalStore you pass in.

## Browser and API clients

Because AuthOptions can hold both a sessionCookie-based session lookup and a jwt or jwtCookie validator at the same time, one createAuth() call can serve cookie-carrying browsers and bearer-token API clients without standing up two separate resolvers. @askrjs/server exposes the resolved value as ctx.auth on every request, so route code written against ctx.auth.principal doesn't need to know which path produced it. That's also what lets the same requirePermission()/requireUser() checks apply uniformly to both kinds of caller.

## Failure behavior

A lookup that fails to identify anyone resolves to authenticated: false with principal and session set to null rather than throwing, so handlers can inspect ctx.auth without try/catch. Turning that into an actual accept/reject decision is the job of the requirement functions covered under Authorization Requirements, which map it to an AuthDecision of {allowed: false, reason: 'unauthenticated' | 'forbidden' | 'already_authenticated'}. A malformed bearer-header JWT and a malformed JWT cookie behave the same way: both go through the JWT validator, and a JwtValidationError from either path is caught internally, so resolution falls through to an anonymous context instead of throwing out to your route.

## Documentation navigation

[Previous](https://askrjs.com/docs/server/probes/index.md) | [Next](https://askrjs.com/docs/authentication/model-and-sessions/index.md)
