Client ID Metadata Documents
A Client ID Metadata Document, or CIMD, removes the registration step. Your
client_id is an https URL. That URL serves a small JSON document
describing your application. SAG fetches it when your application first
appears.
Nothing is registered anywhere, because the document's URL is the identity. Only somebody who controls that origin can change what your application claims to be.
The shortest possible one
Serve this at https://ledger.example.com/oauth-client, with
Content-Type: application/json:
{
"client_id": "https://ledger.example.com/oauth-client",
"client_name": "Ledger",
"redirect_uris": ["https://ledger.example.com/auth/callback"]
}
Then use that URL as your client_id. That is the entire onboarding process.
The rules
SAG will refuse a document that breaks any of these:
- The URL is the client ID. SAG uses the URL it fetched as your client
ID. The
client_idfield is optional and may name a different client, such as a native application that redirects to localhost. - There must be a well-formed redirect URI. It is matched exactly when SAG returns an authorisation code. A metadata publisher is trusted to declare where its own codes go.
- No redirects while fetching. SAG will not follow one. If it did, an open redirect could make a permitted URL serve metadata from elsewhere.
- There is a size cap. The document is small by nature, and a cap means a hostile or broken URL cannot be used to exhaust the fetcher.
- A
jwks_uristays on your origin. This stops the metadata document making SAG fetch token-authentication keys from somewhere else. - The host must be a public one. SAG resolves the URL's A and AAAA
records before fetching, and refuses the document if any answer is
loopback, private, link-local, or a private address tunnelled inside an
IPv6 one. A
client_idthat only resolves inside somebody's network is a way to make SAG fetch from inside its own. Development mode permitslocalhostand nothing else private.
Such a client is public
The document is readable by anybody, so it can hold no secret. That is not a limitation to work around; it is what the design says. SAG therefore requires PKCE from a CIMD client whatever the document says about it.
If your application needs to authenticate at the token endpoint, publish a
jwks or jwks_uri in the document and use private_key_jwt. That is the
only way a self-describing client stops being public, and it is the right
answer for anything long-lived:
{
"client_id": "https://ledger.example.com/oauth-client",
"client_name": "Ledger",
"redirect_uris": ["https://ledger.example.com/auth/callback"],
"token_endpoint_auth_method": "private_key_jwt",
"jwks_uri": "https://ledger.example.com/oauth-client/jwks.json"
}
The private half never leaves your application, and no shared secret ever crosses the wire.
Fields worth setting
| Field | Why |
|---|---|
client_name | Shown to the person: "Continue to Ledger". Set it, or they see a URL |
redirect_uris | Required. Matched exactly |
post_logout_redirect_uris | Where sign-out may return to |
tos_uri, policy_uri | Your terms and privacy links, shown on the sign-in screens |
logo_uri | Your logo, shown above the sign-in heading. https only |
jwks_uri or jwks | Only if you are using private_key_jwt |
The one practical constraint
The client_id URL has to resolve to the same place twice: once for the
browser following a redirect, and once for SAG fetching the document
server-side. Anything that makes those two differ will break it.
In practice this catches people out with split-horizon DNS, a document behind a VPN or an IP allowlist, or a local development hostname that only exists on the developer's machine. If SAG cannot fetch the document from where it runs, the client does not exist as far as SAG is concerned - and a hostname that resolves to a private address is refused outright rather than attempted, as above.
For local development against your own instance, run SAG and the relying party where each can reach the other. The local stack demonstrates exactly this, with a CIMD client as one of four applications signing in.
Caching, and changing the document
SAG caches a fetched document for a short period, so an edit is not
instantaneous. Adding a redirect URI is not a change to make five minutes
before you need it. On your own deployment the
cache lifetime is CLIENTS_CIMD_CACHE_TTL; on the hosted gateway it is
whatever RESOAuth® has configured.
If CIMD does not fit
CIMD is not compulsory in general, but it is the only way to connect an application to the shared hosted gateway today - there is no manual registration route. See beyond a public client for what that rules out, and how to ask about it.
On your own deployment, the same four ways of describing a relying party are
in the relying parties reference, along
with every CLIENTS_CIMD_* switch that controls this behaviour.