Sign in with TillAuth.
OIDCTillAuth is an OpenID Connect provider. Your users already have accounts in a TillAuth app — now your own consoles, dashboards and back-end services can sign them in with the standard authorization code flow. Any OIDC-compliant client library works; no TillAuth SDK required.
Endpoints
Point your OIDC client at the discovery document and it configures itself. An app that signs with its own key (every new app, and older apps once switched under Settings → Token signing) has its own issuer, https://auth.tilldev.dev/realms/<app id>, and id_tokens are signed with that app's key. The dashboard's Clients page and tilldev auth clients <app> print the discovery URL to use.
https://auth.tilldev.dev/realms/<app id>/.well-known/openid-configurationApps still on the shared key use the issuer https://auth.tilldev.dev and https://auth.tilldev.dev/.well-known/openid-configuration. Switching an app changes the issuer its id_tokens carry, so update each client's issuer when you switch.
| Endpoint | URL |
|---|---|
| authorization_endpoint | https://tilldev.dev/oidc/authorize |
| token_endpoint | https://auth.tilldev.dev/v1/oidc/token |
| userinfo_endpoint | https://auth.tilldev.dev/v1/oidc/userinfo |
| end_session_endpoint | https://tilldev.dev/oidc/logout |
| jwks_uri | https://auth.tilldev.dev/realms/<app id>/.well-known/jwks.json |
Supported: code response type, authorization_code and refresh_token grants, EdDSA id_token signatures, PKCE with S256, and the prompt values none and login.
Register a client
Each relying party (a console, a service) is registered against the TillAuth app whose users it signs in. Register one with the CLI:
# confidential client (has a back end that can keep a secret)
tilldev auth clients <app> create "Admin Console" \
--redirect https://ops.acme.com/auth/callback \
--scopes openid,email,profile
# → prints client_id and client_secret (the secret is shown once)For a single-page app or native client that can't hold a secret, add --public — it authenticates with PKCE instead of a secret. List and remove clients with tilldev auth clients <app> and tilldev auth clients <app> rm <client_id>.
| Setting | Notes |
|---|---|
| redirect_uris | Exact-match allow-list. A redirect must equal a registered URI character-for-character — no wildcards. |
| scopes | Subset of openid email profile groups offline_access. openid is always included. |
| client_secret | Shown once at creation for confidential clients. Store it in your app config; rotate by creating a new client. |
| client type | confidential (back-end) or public (PKCE-only, no secret). |
The authorization code flow
PKCE is required. A typical flow:
- Your client generates a
code_verifier+code_challenge(S256) and redirects the browser to the authorization endpoint withclient_id,redirect_uri,scope,state,nonce, and the challenge. - The user signs in on the TillAuth-hosted page (their password, passkey, MFA — whatever the app allows) and approves the requested scopes. Someone already signed in on that browser goes straight through on their SSO session; add
prompt=noneto check without showing anything. - TillAuth redirects back to your
redirect_uriwith a single-usecodeand yourstate. - Your back end exchanges the code at the token endpoint (with the
code_verifier, and the client secret for confidential clients) for anid_token,access_token, andrefresh_token.
POST https://auth.tilldev.dev/v1/oidc/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=<code>
&redirect_uri=https://ops.acme.com/auth/callback
&code_verifier=<verifier>
&client_id=<client_id>
&client_secret=<client_secret> # confidential clients onlyid_token the way any OIDC library does: signature against the JWKS, iss equals the issuer, aud equals your client_id, nonce matches, and it isn't expired. Use the access_token as a Bearer token against the userinfo endpoint.Scopes & claims
Scopes govern what the id_token and userinfo release. Request only what you need — the userinfo endpoint returns exactly the consented claims.
| Scope | Releases |
|---|---|
openid | Required. The subject identifier (sub) — a stable user id. |
email | email and email_verified. |
profile | name (the display name). |
groups | groups: the user's group names, sorted, up to 100; groups_overage: true when there are more. In the id_token and userinfo. |
offline_access | A refresh token, so you can keep the session alive without re-prompting. |
GET https://auth.tilldev.dev/v1/oidc/userinfo
Authorization: Bearer <access_token>
→ { "sub": "…", "email": "ada@acme.com", "email_verified": true, "name": "Ada Lovelace" }Consent & logout
Consent. A client can require a consent screen the first time a user grants it a set of scopes; once granted, later sign-ins skip the prompt unless the client asks for a scope the user hasn't seen before. Trusted first-party clients can be configured to skip consent entirely (--no-consent at registration).
Logout. Send the user to the end-session endpoint to finish an RP-initiated logout. It ends their SSO session on that browser and every session it started. Pass the user's id_token as id_token_hint to sign them out without a question; without it they're asked first. A post_logout_redirect_uri must be registered on the client for TillAuth to redirect back to it.
GET https://tilldev.dev/oidc/logout
?id_token_hint=<id_token>
&post_logout_redirect_uri=https://ops.acme.com/goodbye
&state=<state>The earlier address, https://auth.tilldev.dev/v1/oidc/logout, redirects there. See SSO session for prompt=none errors and how auth_time is set.