Provider identity
Before a relying party can render a “Sign in with {Provider}” button, it needs three things this server alone knows: what the provider is called, what it looks like, and which sign-in methods are actually wired up here.
That is one public document.
curl -s https://auth.civicgate.org/api/v1/provider{ "slug": "civic-gate", "name": "CivicGate", "shortName": "CivicGate", "iconUrl": "https://www.civicgate.org/icon-192.png", "backgroundColor": "#0f1115", "color": "#e6e6ea", "issuer": "https://auth.civicgate.org", "methods": ["password", "oidc", "fedcm"]}Access
| Path | GET /api/v1/provider |
| Alias | GET /.well-known/auth-provider — identical response |
| Auth | None. Public. |
| CORS | Access-Control-Allow-Origin: * |
| Cache | Cache-Control: public, max-age=300 |
Both paths serve the same handler; use whichever suits the caller. The
.well-known alias is the one to reach for when you only know a
provider’s origin.
It is deliberately credential-free: * CORS with no cookie involved, so
any page anywhere can read it to build a button, and there is nothing in
it worth protecting.
Fields
| Field | Type | Always present | What it is for |
|---|---|---|---|
slug | string | yes | Stable machine id. Use it as a config key / CSS hook — never as display text. |
name | string | yes | Full display name. "Sign in with {name}". |
shortName | string | omitted when unset | Compact label for narrow buttons and mobile. Fall back to name. |
iconUrl | string | omitted when unset | Logo. Also the FedCM chooser icon: Chrome wants ≥25px passive / ≥40px button mode. |
backgroundColor | string | omitted when unset | Button/chooser background. |
color | string | omitted when unset | Button/chooser foreground. |
issuer | string | omitted when unset | The OIDC issuer origin. Everything else — discovery, JWKS, the FedCM config URL — is derived from it. |
methods | string[] | yes | Which sign-in paths are live on this deployment. |
Note the casing: this document is camelCase (shortName,
iconUrl, backgroundColor), unlike the snake_case OIDC payloads
elsewhere. It is a UI document, not an OAuth one.
Fields that are unset are omitted, not returned empty. Code against
?. and supply your own fallbacks.
methods — render only what will work
The whole reason this endpoint exists. methods reports what is
actually wired on this deployment, so a client renders only buttons
that will work:
| Value | Present when | The RP should |
|---|---|---|
password | always | Offer a credentials form — first-party apps only. |
oidc | A usable OIDC signing key + client store exist | Offer the redirect/popup flow. |
fedcm | Same condition | Try navigator.credentials.get() first, with a fallback. |
const p = await fetch(`${AUTH_ORIGIN}/api/v1/provider`).then(r => r.json());
if (p.methods.includes('fedcm')) { /* try the browser API first */ }if (p.methods.includes('oidc')) { /* the universal fallback */ }Two honest caveats:
oidcandfedcmcurrently move together. Both are gated by the same condition — a usable signing key plus a client store — so today you will see either["password"]or["password","oidc","fedcm"]. Test for each one anyway; the array is the contract, the coupling is an implementation detail.fedcminmethodsdoes not mean the browser supports it. It means the server offers it. Chrome and Edge support FedCM; Safari and Firefox do not fully. The RP still feature-detects ('IdentityCredential' in window) and still needs the popup fallback. See FedCM → the limits.
One provider per installation
This server is the provider. There is no per-tenant provider switch and no directory of providers to choose from — the document describes this deployment. Two providers means two installations.
That is why an unconfigured deployment does not guess:
{ "slug": "auth", "name": "Auth Server", "issuer": "https://…", "methods": ["password"] }Generic and unbranded on purpose. An unconfigured deployment should look unconfigured, rather than silently wearing someone else’s name.
Configuration
Set these in the auth-server’s environment. Each falls back to the older
FEDCM_BRAND_* variable — those were the FedCM chooser’s branding
before the provider document existed and are still honoured — and then
to a generic default.
| Variable | Falls back to | Final default |
|---|---|---|
AUTH_PROVIDER_NAME | FEDCM_BRAND_NAME | "Auth Server" |
AUTH_PROVIDER_SLUG | derived by slugifying the resolved name | "auth" |
AUTH_PROVIDER_SHORT_NAME | — | (omitted) |
AUTH_PROVIDER_ICON_URL | FEDCM_BRAND_ICON_URL | (omitted) |
AUTH_PROVIDER_BACKGROUND | FEDCM_BRAND_BACKGROUND | (omitted) |
AUTH_PROVIDER_COLOR | FEDCM_BRAND_COLOR | (omitted) |
issuer | — from OIDC_ISSUER | — |
methods | — computed, not configurable | ["password"] |
Slugification lowercases, keeps [a-z0-9], collapses every other run of
characters to a single -, and trims leading/trailing dashes. So
"Test Organization Name" → test-organization-name.
Worked example
A deployment for an organisation called Test Organization Name, displayed compactly as TestOrgName:
# Identity of this providerAUTH_PROVIDER_SLUG=testorgnameAUTH_PROVIDER_NAME="Test Organization Name"AUTH_PROVIDER_SHORT_NAME=TestOrgNameAUTH_PROVIDER_ICON_URL=https://www.testorgname.com/icon-192.pngAUTH_PROVIDER_BACKGROUND=#101418AUTH_PROVIDER_COLOR=#f2f4f8
# Federation endpoints — set these too, or you inherit CivicGate'sOIDC_ISSUER=https://auth.testorgname.comOIDC_LOGIN_URL=https://www.testorgname.com/loginAUTH_PROVIDER_SLUG is set explicitly here rather than left to the
slugifier: the derived value would have been the long
test-organization-name, and the slug is the key everything else keys
off. Set it once, deliberately, and it never drifts.
Result:
{ "slug": "testorgname", "name": "Test Organization Name", "shortName": "TestOrgName", "iconUrl": "https://www.testorgname.com/icon-192.png", "backgroundColor": "#101418", "color": "#f2f4f8", "issuer": "https://auth.testorgname.com", "methods": ["password", "oidc", "fedcm"]}Which renders as “Sign in with Test Organization Name” on a wide button and “Sign in with TestOrgName” on a narrow one — from one fetch, with no provider name hard-coded in the RP.
Verify a deployment
curl -s https://auth.testorgname.com/api/v1/provider | jqcurl -s https://auth.testorgname.com/.well-known/auth-provider | jq # identical
# Headers: expect access-control-allow-origin: * and max-age=300curl -sI https://auth.testorgname.com/api/v1/provider \ | grep -iE 'access-control|cache-control'Check three things: name is yours (not the built-in default),
issuer points at this host, and methods lists what you expect.
Read next
- Sign in with {Provider} — this document driving a real button.
- FedCM — where
brandingis reused for the browser’s chooser. - OIDC provider — what
issuerunlocks.