AuthServer
Todo Sample
Run the hosted-first SqlOS sample with simple FGA, protected-resource metadata, and public-client onboarding paths.
The Todo sample is the canonical "ship hosted auth plus simple FGA fast" walkthrough.
It keeps the model intentionally small:
- hosted auth first
- one user = one tenant boundary
- one tenant resource with child todo resources
- SQL-backed FGA list filtering and simple auth checks in minimal APIs
- headless follow-on when you need custom UI
- a protected resource with audience enforcement
- protected-resource metadata for MCP clients
- preregistered local development with
todo-local - portable public-client onboarding with
CIMDand optionalDCR
Run it#
dotnet run --project examples/SqlOS.Todo.AppHost/SqlOS.Todo.AppHost.csprojOpen http://localhost:5080/.
If you already ran an older version of the Todo sample, reset the Todo sample SQL database or persistent volume once before rerunning. Existing rows are not backfilled into the FGA graph.
What the sample includes#
Hosted first#
The default path is a seeded browser client called todo-web.
Use the landing page to:
- start hosted sign in
- start hosted sign up
- land in a tiny Todo UI backed by the sample API
This keeps the first experience simple. You do not have to write a custom auth UI to get a working OAuth flow.
Simple FGA#
The Todo sample now models every todo as an FGA resource.
Hierarchy:
roottenant::{userId}todo::{todoId}
Role and permission matrix:
tenant_ownerTENANT_CREATE_TODOTODO_READTODO_WRITE
The app grants tenant_owner on the user tenant node. Child todo resources inherit access from that node, so the sample can:
- list todos with
GetAuthorizationFilterAsync<TodoItem>(...) - authorize create against the tenant node
- authorize toggle/delete against the todo resource
That keeps the controller code small while still populating the FGA dashboard with a real tree.
Headless follow-on#
When you want app-owned auth screens, flip:
TodoSample__EnableHeadless=trueThe sample then redirects /sqlos/auth/authorize into headless.html, which drives the same authorization request through the SqlOS headless APIs.
That lets you compare:
- hosted setup for the fastest first version
- headless setup for custom branding and forms
If your headless UI runs on a different origin than the SqlOS host, send credentialed browser requests to the headless endpoints so SqlOS can persist its reusable auth-page session cookie. Then follow-up /sqlos/auth/authorize?prompt=none requests can silently complete when that session exists, or return login_required when it does not.
Protected resource behavior#
The sample API protects /api/todos with audience-aware token validation.
It also exposes:
GET /.well-known/oauth-protected-resource
And when a caller presents no token or the wrong audience, the API returns:
401 Unauthorized- a
WWW-Authenticateheader withresource_metadata
That means the sample demonstrates both sides at once:
- protected-resource metadata for MCP/public-client flows
- simple FGA subject sync, grants, hierarchy, and SQL-backed authorization
Local preregistered client#
For localhost development, use the seeded client:
client_id:todo-local- redirect URI:
http://localhost:3100/oauth/callback - auth method: public PKCE only
This is the easiest local loop before you publish the sample on a stable HTTPS origin.
Local Emcy broker client#
For the Emcy-hosted local Todo MCP flow, use the additional seeded client:
client_id:todo-mcp-local- redirect URI:
http://localhost:5150/api/v1/hosted-mcp/todo-local/oauth/callback - auth method: public PKCE only
This client exists so Emcy can broker the downstream Todo authorization flow while the Todo API itself keeps the same OAuth and audience behavior.
Portable public clients#
Once the sample is reachable on public HTTPS, you can use both public-client onboarding paths.
CIMD#
The sample publishes a metadata document at:
/clients/portable-client.jsonThat document is useful as:
- a working reference for the shape of a
client_idmetadata document - a starter path when you want to point a portable client at a stable HTTPS URL
DCR#
DCR stays off by default. Enable it when you need compatibility clients:
TodoSample__EnableDcr=trueThen use:
POST /sqlos/auth/registerThis is the compatibility path for clients that still expect dynamic client registration.
Why this sample exists#
Use the Todo sample when your question is:
"How do I ship hosted auth, simple FGA, and protected-resource metadata with very little app code?"
Use the broader Example stack when your question is:
"How do I explore all of SqlOS, including OIDC, SAML, org workflows, and richer multi-tenant FGA?"