Getting Started with Signet
This guide is for developers integrating an application with an existing Signet deployment. For operator/deployment docs (running the server, env vars, key generation), see the server README.
The audit pages (/admin/audit and /account/audit) automatically load more records as you scroll to the bottom, with Retry and Back to top controls. Previous / Next cursor links remain available without JavaScript; no totals or page numbers are shown. A legacy ?page=N URL alone opens the first cursor page; existing cursor links still work. The retired AUDIT_CURSOR_PAGINATION_ENABLED variable has no effect. The JSON API uses separate forward-only cursors without totals. See the server README for operator documentation.
Signet is an OAuth 2.0 + OpenID Connect authorization server. It issues tokens that your app can use to authenticate users and call protected APIs.
Pick a Flow
| Your application | Recommended flow |
|---|---|
| Server-rendered web app (has a backend) | Authorization Code + PKCE (confidential client) |
| Single-page app (React / Vue / Svelte / etc.) | Authorization Code + PKCE (public client) |
| Mobile or desktop app | Authorization Code + PKCE (public client) |
| CLI tool, IoT device, or headless shell | Device Authorization Grant |
| Backend service calling another service (no user) | Client Credentials |
| Script, CI job, or cron task that can run no flow | Personal API key (sgk_) |
Unsure? Use Authorization Code + PKCE for anything with a user and Client Credentials for service-to-service. A personal API key is the last resort for a caller that can complete no flow at all — it trades short-lived tokens and offline verification for working in a one-line curl.
Before You Integrate
Ask your Signet administrator for:
- Base URL — e.g.
https://your-signet. Everything else you need is reachable fromBASE_URL/.well-known/openid-configuration(see below).
client_id— identifies your application.
client_secret— only for confidential clients (server-side web apps, client-credentials services). Public clients (SPAs, mobile, CLIs) do not get a secret.
- Allowed redirect URIs — for Authorization Code Flow. Signet does exact-string matching:
https://yourapp.example/cbandhttps://yourapp.example/cb/are not the same URI.
- Allowed scopes — which of
openid,profile,email,offline_accessthis client may request. (Your admin may also have registered custom API scopes — ask which ones apply.)
- Enabled grant types — which of Device Flow / Auth Code Flow / Client Credentials are turned on for this client.
- Resource identifier(s) — skip unless you’re integrating an MCP server or a multi-RS deployment that enforces audience binding (RFC 8707). The absolute http(s) URI(s) you’ll pass as
resource=so the issued access token’saudmatches your resource server. Each value must be added to your client’s allowed-resources allowlist by the admin — it’s deny-all by default, so a client with an empty allowlist getsinvalid_targetfor anyresource=it sends.
Start Here: OIDC Discovery
Instead of hardcoding endpoint URLs, fetch the OIDC Discovery document:
curl https://your-signet/.well-known/openid-configuration
{
"issuer": "https://your-signet",
"authorization_endpoint": "https://your-signet/oauth/authorize",
"token_endpoint": "https://your-signet/oauth/token",
"userinfo_endpoint": "https://your-signet/oauth/userinfo",
"revocation_endpoint": "https://your-signet/oauth/revoke",
"jwks_uri": "https://your-signet/.well-known/jwks.json",
"response_types_supported": ["code"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"],
"scopes_supported": ["openid", "profile", "email"],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post",
"none"
],
"grant_types_supported": [
"authorization_code",
"urn:ietf:params:oauth:grant-type:device_code",
"refresh_token",
"client_credentials"
],
"claims_supported": [
"sub",
"iss",
"aud",
"exp",
"iat",
"jti",
"auth_time",
"nonce",
"at_hash",
"name",
"preferred_username",
"email",
"email_verified",
"picture",
"updated_at"
],
"code_challenge_methods_supported": ["S256"],
"authorization_response_iss_parameter_supported": true
}
Most mature OAuth/OIDC libraries can consume this document directly and wire up the flow for you.
A few gotchas with this document:
jwks_uriandid_token_signing_alg_values_supportedare only present when Signet is configured for RS256/ES256 (asymmetric signing). On HS256 deployments they’re omitted.
/oauth/introspectand/oauth/device/codeare supported but not advertised in Discovery — use the paths shown in this guide directly.
scopes_supportedonly lists the built-in OIDC scopes (openid,profile,email).offline_accessand any custom API scopes your admin registered for a client are accepted when requested even though they aren’t advertised here — ask your admin which ones apply.
Non-OIDC / MCP clients: Signet also publishes an RFC 8414 authorization server metadata document at
/.well-known/oauth-authorization-server. It advertisesresource_indicators_supported: trueanddevice_authorization_endpoint, and is the document MCP and OAuth-2.1-only clients expect. The two documents share fields where they overlap; pick whichever your library wants.
Supported Scopes
| Scope | Purpose |
|---|---|
openid |
Required to receive an ID token and use /oauth/userinfo |
profile |
Unlocks name, preferred_username, picture, updated_at on UserInfo/ID token |
email |
Unlocks email, email_verified on UserInfo/ID token |
offline_access |
Signals that you want a refresh token (OIDC Core §11) |
Notes:
openidandoffline_accessare not valid in the Client Credentials flow (rejected).
- A client can only request scopes the administrator registered for it.
- Scopes are sent as a space-separated string (
scope=openid profile email).
Tokens at a Glance
After a successful flow, Signet issues:
- Access token — JWT; short-lived; include as
Authorization: Bearer <token>on API calls.
- Refresh token — opaque; longer-lived; trade for a new access token at
/oauth/token.
- ID token — JWT about the user (only when
scopecontainsopenid). See OpenID Connect.
Access token lifetime varies per client (short ≈ 15m, standard ≈ 10h, long ≈ 24h). Always honor the expires_in field of the token response — never hardcode a duration.
When you pass resource=<URL> on token requests, the access token’s aud claim is bound to that resource (RFC 8707) — provided the value is on your client’s allowlist (deny-all by default). Resource servers should validate aud against their own identifier — see JWT Verification §Audience Binding.
Rate limits, revocation, introspection, refresh rotation: see Tokens & Revocation.
A Minimal Integration Checklist
- [ ] Confirm
BASE_URL,client_id, (client_secret?), redirect URIs, and scopes with your admin.
- [ ] Fetch
/.well-known/openid-configurationat startup; cache it.
- [ ] Pick a flow and wire it up (see the per-flow docs below).
- [ ] Verify tokens at your resource servers using JWKS (JWT Verification).
- [ ] Handle the common OAuth errors (Errors).
- [ ] Implement sign-out: call
/oauth/revokewith the refresh token (Tokens & Revocation).
- [ ] If the client is public and long-lived, use PKCE (
S256— the only method Signet accepts).
Next Steps
- Authorization Code Flow + PKCE — Web, SPA, and mobile apps
- Device Authorization Flow — CLI and headless clients
- Client Credentials Flow — Service-to-service
- API Keys — Opaque
sgk_keys for scripts and CI that can’t run a flow
- OpenID Connect — ID tokens and UserInfo
- JWT Verification — Verify access tokens at resource servers
- Tokens & Revocation — Refresh, revoke, introspect
- Errors — OAuth error codes and how to handle them
Reading management lists
Clients, users, tokens, authorizations and app API-key lists load more records as
you scroll. Search and filters restart at the latest matching records. Use Retry
after a failed load, Latest records to restart, or Back to top to return to the
heading without discarding loaded rows. Without JavaScript, Load more opens the
next batch. These lists do not show exact totals or page numbers; old page-number
bookmarks return to the filtered first batch. A cursor that no longer matches your
account, target or filters offers a restart link.
List timestamps are stored and compared in UTC+0. The existing browser-local time
display still shows your timezone. Operators upgrading SQLite must stop old writers
and complete UTC normalization before serving cursor lists; see the repository’s
Configuration guide for automatic/manual migration and rollback instructions.