Authentication
Authentication answers the question "who is making this request?" Manzano handles authentication in three layers — the browser, the edge, and your backend — and hands your Grove actions a validated identity in
ctx.principalwithout ever exposing the raw token to application code.
Prerequisites: Familiarity with the Concepts Overview. What you'll learn: The three-layer authentication model, what Manzano does for you, and where to go to configure each piece.
Authentication vs. Authorization
These two words are often used interchangeably. In Manzano they are separate concerns with separate docs:
| Concern | Question | Where it's configured |
|---|---|---|
| Authentication | Who is making this request? | This section (authentication/*) |
| Authorization | Is this principal allowed to do X? | Authorization Hooks and per-project Cedar policies |
Authentication produces a ctx.principal. Authorization consumes it. If you are writing an authorize hook or a Cedar policy and wondering where ctx.principal.id comes from, you are in the right place.
The Three Layers
Every request to a Manzano-hosted app passes through three tiers. Each tier has a well-defined responsibility.
Layer 1 — Browser
The user's browser holds one long-lived cookie: the ID token, issued by whichever identity provider you have configured for your project. The browser never talks to your backend directly for login; it talks to Manzano's edge, which in turn talks to the identity provider.
Layer 2 — Edge
The edge worker sits in front of your backend and is responsible for:
- Detecting anonymous requests to routes that require authentication and redirecting them to the login flow.
- Validating the ID-token JWT on every authenticated request (signature check against the provider's JWKS, audience, issuer, expiry).
- Extracting claims from the JWT and stamping them as HTTP headers on the request it forwards to your backend (
X-Sub,X-Email, and others — see Identity flow). - Signing the entire request (headers + path + body digest) with an HMAC shared secret so your backend can prove the headers really came from the edge and not a client spoofing them.
Because the edge does the JWT work, your application code never validates tokens, never pulls JWKS, and never touches PKCE. That work is done once per request, at the edge, in a place that is always up to date.
Layer 3 — Backend (your Grove code)
Your Grove backend receives the request with identity headers already set and verified. The runtime:
- Verifies the edge's HMAC signature against a shared secret.
- Populates
ctx.principalfrom the identity headers. - Runs any
authorizehook and evaluates any Cedar policy. - Invokes the action.
In your action body, ctx.principal.id is simply the sub claim the identity provider issued — no extra configuration needed.
What Manzano does for you vs. what you configure
When you deploy a Manzano app, most of the machinery is automatic:
| Piece | Handled by Manzano | Your choice |
|---|---|---|
| Edge JWT validation | ✅ | — |
| JWKS fetching & caching | ✅ | — |
| Claim-to-header mapping | ✅ (defaults work) | Optional overrides |
| HMAC request signing | ✅ | — |
| Login UI (chooser page) | ✅ | Optional theming |
| Session cookie handling | ✅ | Cookie name is tenant-configurable |
| Identity provider choice | — | You pick |
| Which routes require auth | — | You pick (see below) |
Identity providers
Manzano supports two flavors of identity provider. You pick one (or one per environment) when you enable auth on your project.
External OIDC providers — Auth0, Clerk, WorkOS, Google, Apple, or any custom OIDC-compliant provider. You register your Manzano app in the provider's dashboard, paste the credentials into your project config, and the edge handles the rest.
Built-in email magic link — No external provider needed. The user enters their email on the login page, receives a one-time link, clicks it, and is signed in. Manzano mints its own short-lived JWT signed with a key it manages. Good for early-stage projects that want login without the Auth0 setup cost.
Both flavors are covered in Identity Providers, including per-provider setup steps.
Enabling auth on a project
Once you have picked a provider, enabling authentication is a matter of configuring it on your project and redeploying. See Enabling Authentication for the step-by-step.
One important behavior to know up front: when auth is configured, every dynamic route in your app requires authentication. Static assets stay public; your route.grove handlers and any [param] routes do not. This is all-or-nothing today; per-route opt-in is a planned future feature.
Anonymous routes are still possible — your Cedar policy can allow Role::"__anonymous" on specific actions. See the Authorization Hooks page for how that interacts with authentication state.
When you don't need auth
Many Grove projects start without authentication — workflow backends, webhook receivers, scheduled jobs, and read-only public sites all work fine without a principal. In that case:
- Skip this entire section.
ctx.principalisnullin your action handlers.- Cedar's
Role::"__anonymous"sentinel fires on every request (if you have apolicies.cedarfile).
You can add auth later without rewriting your actions.
Where to go next
- Enabling Authentication — the step-by-step for turning on login.
- Identity Providers — what each supported provider needs.
- Identity Flow — the full end-to-end: how a header becomes a
ctx.principal. - Authorization Hooks — what to do with the authenticated identity.
See Also
- Authorization Hooks — the
authorizehook andctx.principalAPI. - Subdomains & Edge Routing — how tenant resolution and edge routing work.
- Request Context — everything else available on
ctx.