Optional — OIDC mode only. This applies when
AUTH_MODE=oidc. The default mode isproxy(the app trusts a reverse-proxy SSO header and needs no Keycloak); see proxy-auth.md. Keycloak is just one OIDC provider — any works.
When AUTH_MODE=oidc, Osnova authenticates users via OpenID Connect (authorization-code
flow with PKCE). Identities are federated into Osnova on first login; access is then
governed by per-workspace roles (see administration.md).
Create a realm named osnova (the name is referenced by KEYCLOAK_ISSUER).
Create a client used by the Osnova web app:
| Setting | Value |
|---|---|
| Client ID | frontend (matches KEYCLOAK_CLIENT_ID) |
| Client type | OpenID Connect |
| Authentication | Public (PKCE) is fine; confidential also works — then set KEYCLOAK_CLIENT_SECRET. |
| Standard flow | Enabled (authorization code) |
| Valid redirect URIs | ${APP_URL}/api/auth/callback — e.g. http://localhost:3000/api/auth/callback |
| Valid post-logout redirect URIs | ${APP_URL}/ |
| Web origins | ${APP_URL} |
The app requests the openid profile email scopes. The sub and email claims are
required; name is used for display when present.
Osnova passes the user's chosen locale to Keycloak via the ui_locales parameter, so
the login page renders in the same language as the app (Polish, English, German). For
that to take effect, enable internationalization on the realm:
Realm settings → Localization → Internationalization = On, supported locales
pl, en, de, default pl.
Via the Admin REST API (token from a master-realm admin client):
curl -X PUT "https://<keycloak>/admin/realms/osnova" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"internationalizationEnabled":true,"supportedLocales":["pl","en","de"],"defaultLocale":"pl"}'A successful update returns HTTP 204.
Set ADMIN_EMAILS (comma-separated). On first login, a user whose email matches is
granted the system_admin global role — letting them create workspaces and bind
repositories.
Internal users can sign in with their Microsoft work account. Keycloak brokers to Microsoft Entra ID: the login screen gets a "Microsoft" button → Entra → back to Keycloak → back to Osnova. Osnova itself is unchanged (it always federates through Keycloak).
Azure side (App Registration in the hycom tenant):
- Single-tenant (the hycom directory).
- Add Keycloak's broker callback as a Web redirect URI:
https://<keycloak>/realms/osnova/broker/microsoft/endpoint - Create a client secret (Certificates & secrets) and note the Application (client) ID and Directory (tenant) ID.
Keycloak side — an OIDC identity provider with alias microsoft:
- Endpoints from
https://login.microsoftonline.com/<TENANT-ID>/v2.0. - Client authentication
client_secret_post, PKCE (S256), scopesopenid profile email. trustEmailon; attribute mappers foremail,given_name→firstName,family_name→lastName.- Auto-link by email: a first-broker-login flow that silently links a Microsoft identity to an existing Keycloak user with the same (trusted) email. Verify against a real sign-in before enabling for everyone.
The IdP can be created/updated via the Admin REST API
(/admin/realms/osnova/identity-provider/instances) or in the Admin Console under
Identity providers. Keep it disabled until the client secret is set, then enable
to surface the button on the login screen.
GET /api/auth/logingenerates PKCE verifier + state, reads the locale cookie, and redirects to Keycloak withui_locales.GET /api/auth/callbackvalidates state + verifier, exchanges the code, readssub/email/name, find-or-creates the Osnova user, mints theosnova_sessionJWT (8 h), and stores the locale cookie.GET /api/auth/logoutclears the session cookie.
Source: src/app/api/auth/{login,callback,logout}/route.ts, src/lib/auth/keycloak.ts.
| Symptom | Cause / fix |
|---|---|
| Redirected back to login repeatedly | Redirect URI mismatch — it must equal ${APP_URL}/api/auth/callback exactly (scheme, host, port, path). |
invalid_grant on token |
Wrong user credentials, or expired code. |
unauthorized_client / "Invalid client or Invalid client credentials" |
The client is confidential but no/invalid secret was sent. Set KEYCLOAK_CLIENT_SECRET, or make the client public. |
Client not allowed for direct access grants |
You tried a password grant on a client (e.g. security-admin-console) that only allows the browser code flow. Use a client with Direct Access Grants enabled, or use the browser flow. |
| Login screen ignores language | Internationalization not enabled on the realm (step 3), or the locale isn't in supportedLocales. |
Automating realm changes needs a token from a master-realm admin client. On some
deployments admin-cli is configured as confidential and requires a client_secret
(Clients → admin-cli → Credentials); security-admin-console rejects the password
grant. Use a master-realm client with Direct Access Grants enabled (plus its secret if
confidential), or simply use the Admin Console UI.
Next: Feature guide »