OpenID Connect (OIDC)

Linux Directory as an OpenID Connect identity provider.

OpenID Connect (OIDC)

What OIDC is

OpenID Connect is an identity layer on top of OAuth 2.0. An application (relying party) sends the user to an identity provider (IdP). After the user signs in, the IdP returns:

  • an ID token (JWT) asserting who the user is (sub, name, email, …)
  • usually an access token for calling APIs (e.g. UserInfo)
  • optionally a refresh token to obtain new tokens without re-login

That is how apps like Grafana, Nextcloud, or internal portals do SSO against a central directory — without each app storing passwords.

In lindir, the server is the IdP. Domain users authenticate with their lindir password; apps trust lindir’s tokens.

Apps: step-by-step Grafana and Nextcloud (user_oidc) wiring is in Handbook §8. Nextcloud is a natural companion for files, calendar, and contacts.

What lindir implements

Piece Detail
Discovery GET /.well-known/openid-configuration (also /oidc/.well-known/…)
Authorization code GET/POST /oidc/authorize (HTML login form)
Token POST /oidc/token (authorization_code, refresh_token)
UserInfo GET /oidc/userinfo
JWKS GET /oidc/jwks (RSA public key for RS256)
Admin POST/GET /v1/oidc/clients, POST /v1/oidc/clients/revoke
Clients Confidential (client secret) and public ("public": true, no secret)
PKCE Optional for confidential; required for public (S256 / plain)
Signing RS256 ID tokens; key auto-generated at oidcSigningKeyPath

Passkeys / WebAuthn: see WebAuthn.

Config

{
  "issuerUrl": "https://id.example.com",
  "oidcSigningKeyPath": "/var/lib/lindir/oidc-rsa.pem"
}

If issuerUrl is empty, issuer defaults to http://<listenHost>:<listenPort> (0.0.0.0 → 127.0.0.1).
If the PEM file is missing, lindir creates a 2048-bit RSA key (mode 0600) on first start.

Register an app

Confidential (default):

curl -s -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"name":"grafana","redirectUris":["https://grafana.example.com/login/generic_oauth"]}' \
  http://127.0.0.1:1289/v1/oidc/clients

Public / SPA (PKCE required):

curl -s -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"name":"spa","public":true,"redirectUris":["https://app.example.com/cb"]}' \
  http://127.0.0.1:1289/v1/oidc/clients

Or via CLI:

lindir-admin oidc client create --name grafana --redirect-uri 'https://grafana/…'
lindir-admin oidc client create --name spa --redirect-uri 'https://spa/cb' --public

Save clientId and clientSecret (secret shown once; empty for public clients). Point the app at:

  • Issuer / discovery: your issuerUrl
  • Auth: /oidc/authorize
  • Token: /oidc/token
  • UserInfo: /oidc/userinfo
  • JWKS: /oidc/jwks
  • Scopes: openid profile email
  • Token auth: client_secret_basic / client_secret_post (confidential) or none + PKCE (public)

Users need a password set (POST /v1/users with password, or POST /v1/users/password).

Refresh tokens

Token responses include refresh_token. Exchange with:

grant_type=refresh_token&refresh_token=…&client_id=…

Refresh tokens are single-use (rotated on each refresh) and expire after 30 days by default.