Enabling Authentication
How to turn on login for a Manzano project: when you need auth, how it interacts with your routes, how to read identity in Grove actions, and how to log users out.
Prerequisites: Authentication Overview, a deployed Manzano project. What you'll learn: The decision of whether to enable auth, the steps to configure it, how route protection works today, and how to pair it with authorization.
Status: Preview. The end-to-end configuration surface described on this page is firming up. Where the live surface differs from what is documented here, the authoritative references are your
manzano deployoutput and the Identity Providers page. Expect the rough edges to smooth over the next few releases.
When to enable authentication
Start a project without auth. Most Grove work — writing actions, modeling records, running workflows — does not need an authenticated caller. Add authentication when you need to:
- Attribute record changes to a specific user.
- Restrict a page, route, or action to signed-in users.
- Apply per-user authorization through
authorizehooks or Cedar policies.
If none of those apply yet, skip this page. ctx.principal will be null in your actions, and manzano dev will serve every route to any caller.
How login works at a glance
When authentication is enabled on a project:
- An anonymous request to a protected route is redirected by the edge to the configured identity provider.
- The user signs in (OIDC provider, built-in email magic link, etc.).
- The provider redirects back with a signed token. The edge validates the token, extracts claims, and stamps them onto the request as HTTP headers (
X-Sub,X-Email, …). - The edge signs the whole request with an HMAC shared secret and forwards it to your backend.
- Grove reads the identity headers, populates
ctx.principal, and runs your action.
See Identity Flow for the full trust-boundary picture.
All-or-nothing today
When auth is configured on a project, every dynamic route becomes protected. Static assets (files under apps/<app>/static/ and the like) remain public; anything that hits a Grove handler requires an authenticated caller.
Per-route opt-in (for example a [[protect]] directive that lets you mark specific routes as public while keeping the rest authenticated) is planned but not yet shipped. In the meantime, two patterns cover most needs:
- Public routes on a dedicated subdomain. Put the public-facing app on a separate subdomain (
apps/marketing/) without auth configured, and keep the authenticated surface on another (apps/app/). - Anonymous access via Cedar. Leave auth enabled and permit anonymous callers on specific actions or routes using the
Role::"__anonymous"sentinel. See Per-Project Cedar Policy.
Picking a provider
Two flavors of identity provider are supported:
- External OIDC — Auth0, Clerk, WorkOS, Google, Apple, or any standards-compliant OIDC IdP you already use.
- Built-in email magic link — passwordless sign-in managed by Manzano, no external IdP required.
See Identity Providers for per-provider setup and claim-mapping details.
Reading identity in a Grove action
Once authentication is enabled and a user is signed in, every action receives a populated ctx.principal:
action post_comment(ctx, body: String) {
if ctx.principal is null {
fail "Sign in to comment"
}
let comment = Comment {
id: Id.generate(),
author_id: ctx.principal.id,
author_email: ctx.principal.claims.email,
body: body,
created_at: ctx.now,
}
emit CommentPosted { comment: comment }
}
ctx.principal.idis the provider'ssubclaim (for OIDC providers, the stable user identifier; for built-in email, the email address prefixed withemail|).ctx.principal.claims.emailis the user's email, when the provider issues one.ctx.principal.rolesis populated from your declared role vocabulary (see Authorization Hooks).
For deeper authorization beyond "is the user signed in," use an authorize hook or a Cedar policy — see Authorization Hooks and Per-Project Cedar Policy.
Anonymous access
You can still permit anonymous callers on specific actions even with auth enabled. The most straightforward approach is a Cedar policy that targets Role::"__anonymous":
// Anyone can read published posts.
permit (
principal in Role::"__anonymous",
action == Action::"read_post",
resource
)
when { resource.status == "published" };
In an authorize hook, the equivalent pattern is:
authorize read_post(ctx, post_id: Id) {
let post = lookup_post(post_id)
if post.status == "published" {
return // permit, no further checks
}
if ctx.principal is null {
fail "Sign in to view drafts"
}
if post.author_id != ctx.principal.id {
fail "Only the author can view drafts"
}
}
See Authorization Hooks for the full hook model.
Logout
Manzano's edge exposes a logout endpoint for authenticated apps:
GET /auth/logout— clears the ID-token cookie and redirects the user to the provider's logout endpoint (when the provider supports it) or to your app's post-logout page.GET /auth/logout-complete— the default landing page the edge redirects to after logout completes; you can override this per project.
Your Grove code does not need to implement logout. If you want a "Sign out" button in your UI, link it to /auth/logout.
Troubleshooting
| Symptom | Likely cause |
|---|---|
Request 302s to /auth/login even after signing in | ID-token cookie not being sent. Check cookie domain matches the request host. |
Signed-in user shows ctx.principal == null in backend | Token validated at the edge but your action ran before the edge request reached it (cached preview URL, misrouted request). Re-deploy and reload. |
| Infinite redirect loop after sign-in | Provider's configured redirect URL doesn't match the tenant's allowed list. See Identity Providers. |
| "X-Forwarded-Host missing" error | A request bypassed the edge. Only Manzano-hosted deployments are supported; hitting a backend directly won't pass origin-auth HMAC. |
For deeper debugging, the per-layer table in Identity Flow — Debugging authenticated requests maps symptoms to layers.
See Also
- Authentication Overview — browser / edge / backend mental model
- Identity Providers — per-provider configuration and claim mapping
- Identity Flow — edge-stamped headers and trust boundary
- Authorization Hooks —
authorizehooks and Cedar policies