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) ornone+ 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.
