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:

  1. Base URL — e.g. https://your-signet. Everything else you need is reachable from BASE_URL/.well-known/openid-configuration (see below).
  2. client_id — identifies your application.
  3. client_secret — only for confidential clients (server-side web apps, client-credentials services). Public clients (SPAs, mobile, CLIs) do not get a secret.
  4. Allowed redirect URIs — for Authorization Code Flow. Signet does exact-string matching: https://yourapp.example/cb and https://yourapp.example/cb/ are not the same URI.
  5. Allowed scopes — which of openid, profile, email, offline_access this client may request. (Your admin may also have registered custom API scopes — ask which ones apply.)
  6. Enabled grant types — which of Device Flow / Auth Code Flow / Client Credentials are turned on for this client.
  7. 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’s aud matches 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 gets invalid_target for any resource= 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_uri and id_token_signing_alg_values_supported are only present when Signet is configured for RS256/ES256 (asymmetric signing). On HS256 deployments they’re omitted.
  • /oauth/introspect and /oauth/device/code are supported but not advertised in Discovery — use the paths shown in this guide directly.
  • scopes_supported only lists the built-in OIDC scopes (openid, profile, email). offline_access and 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 advertises resource_indicators_supported: true and device_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:

  • openid and offline_access are 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 scope contains openid). 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-configuration at 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/revoke with the refresh token (Tokens & Revocation).
  • [ ] If the client is public and long-lived, use PKCE (S256 — the only method Signet accepts).

Next Steps

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.