Access & end-user sign-in
Every Ignite app has a public URL, https://<app>-<org>.ignite.dodil.cloud (copy it from public_urls in dodil ignite app get <app>). Two settings decide who may call it:
--allow-unauthenticated(public_invoke): whether anonymous callers get through. Owner-pays: anonymous traffic bills your org. Defaultfalse.--user-pool(user_pool): an AppID user pool,org/pool, whose people may sign in to the app. The Ignite gateway runs the login and hands your app a verified identity, so the app contains no auth code. The pool must belong to the app’s own org.
Platform principals (you, your service accounts, CI, dodil ignite invoke) can always call the app with a Dodil token. A user pool adds sign-in; it never removes platform access.
Who gets in
--user-pool | --allow-unauthenticated | Who can call the app |
|---|---|---|
| unset | false | Platform principals only. |
| unset | true | Anyone, with no identity. |
| set | false | Sign-in required. An anonymous browser GET is redirected to /.dodil/auth/login; any other anonymous request gets 401 with a JSON body {"error":"end-user login required","login_url":"…"}. |
| set | true | Optional sign-in. Anonymous requests pass. When the caller has a session or presents a pool token, the identity headers are injected, and the app decides. Use this for an app with an MCP endpoint (see below). |
Set it up
-
Create the pool and invite people (see AppID → Sign-in for Ignite apps for roles, invites, and Google/Microsoft sign-in):
dodil appid pool create acme --display-name "Acme" --with-local dodil appid user create acme [email protected] --roles rep -
Attach the pool to the app:
dodil ignite app create acme-app --user-pool <org>/acme # sign-in required dodil ignite app update acme-app --user-pool <org>/acme # on an existing app dodil ignite app update acme-app --allow-unauthenticated=true # make sign-in optional dodil ignite app update acme-app --clear-user-pool # back to platform-onlyDeploying with Git CI? Set it in
.dodil/deploy.yaml:apps: - name: acme-app user_pool: acme # "pool" (same org) or "org/pool"; "" detaches; absent = no change public_invoke: false # applied only when the app is first createdpublic_invokeindeploy.yamltakes effect only at creation. To change it later, usedodil ignite app update <app> --allow-unauthenticated=<true|false>. -
Allowlist the callback on the pool. The gateway signs people in with the redirect URI
https://<public_url>/.dodil/auth/callback; a pool without it signs nobody in (“redirect_uri is not in the pool’s allowlist”). This is a manual step today:dodil appid settings get acme -o json # settings set REPLACES the object: read first dodil appid settings set acme --redirect-uris https://<public_url>/.dodil/auth/callback -
Verify with a real person: open the app URL in a private window, sign in as an invited user, and check that you land back in the app.
dodil ignite invokeuses your platform token and skips the sign-in, so it can’t prove sign-in works.
Reserved paths
On an app with a user pool, the gateway answers these paths itself; your app never sees them:
| Path | What it does |
|---|---|
/.dodil/auth/login | Starts sign-in (PKCE against the pool). Takes an optional return_to path. |
/.dodil/auth/callback | The pool redirects back here; the gateway sets the session cookie. |
/.dodil/auth/logout | Clears the session cookie, revokes the session at the pool, and redirects to /. |
The session is an HttpOnly, host-only cookie, so page JavaScript never holds a token. Serve the UI and the API from one app (one host) so the cookie reaches both.
Identity headers
On every request that passes, the gateway strips any inbound copy of these headers and then sets them itself:
| Header | Contents |
|---|---|
X-Dodil-User | JSON: sub, email, connection, app_roles |
X-Dodil-User-Jwt | The pool access token the gateway verified. The permissions claim (and tenant, when pinned) is only here. |
X-Dodil-Auth-Source | pool (a person from the pool) or platform (a platform principal) |
A client can’t forge them: the gateway removes them before setting its own. A local dev server does not, so a test that sets X-Dodil-User by hand passes even where production would refuse the request. Check once against the deployed URL.
The headers reach apps that run a container (image or Git builds). See AppID → Read the identity in your handler for a FastAPI example.
MCP endpoints
If your app serves an MCP endpoint that people connect from Claude.ai or ChatGPT, use --user-pool with --allow-unauthenticated=true. The assistant’s first requests are anonymous discovery calls, and with sign-in required the gateway refuses them before your app can answer. Today the app itself must:
- serve
GET /.well-known/oauth-protected-resource/mcpnaming the pool issuerhttps://appid.dodil.io/<org>/<pool>; - answer an unauthenticated
/mcpwith401andWWW-Authenticate: Bearer resource_metadata="<base>/.well-known/oauth-protected-resource/mcp"; - refuse every other route that arrives without
X-Dodil-User.
The full walkthrough, including client approval in AppID, is in AppID → A person connecting Claude.ai or ChatGPT .