Authentication & OIDC¶
Portalcrane supports three ways to authenticate: the built-in admin account, local username/password accounts, and OpenID Connect (OIDC / SSO). All three can coexist.
Real passwords work for the web UI, never for docker login
A local account's password (below) and OIDC both authenticate the
web UI and REST API. The registry proxy (docker login /
pull / push) is a separate surface with its own rule: only a
Docker-scoped Personal Access Token
is accepted there — a real password is always rejected, even for the
built-in admin. See the
Docker CLI Walkthrough.
Local authentication¶
- Built-in admin — the
adminaccount (configurable viaADMIN_USERNAME), whose password is auto-generated on first launch and printed once in the container logs. It cannot be deleted, renamed, or demoted; only its password can be changed. - Local users — created by an admin under Settings → Accounts, each with a username/password pair. See Users, Groups & Permissions for the full account model.
Login is a simple JSON POST:
curl -X POST http://<host>:8000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "<password>"}'
The browser flow additionally receives the JWT in an HttpOnly cookie
(pc_token by default), so the Angular app never touches the token
directly; API clients can use the access_token from the JSON response as
a Bearer token instead.
Any authenticated user can change their own password:
curl -X POST http://<host>:8000/api/auth/me/password \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"current_password": "old", "new_password": "at-least-8-chars"}'
OIDC-provisioned accounts have no local password and this endpoint rejects them — their identity is managed entirely by the external provider.
OIDC (Single Sign-On)¶
Set OIDC_ENABLED=true and the standard OIDC fields
(OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_REDIRECT_URI,
…) to add an SSO login option alongside local login. All values can also be
set from Settings → OIDC — see the full variable list in
Environment Variables → OIDC.
Just-in-time provisioning¶
The first time a user completes the SSO flow, Portalcrane creates a local
record for them (auth_source: "oidc", no password hash). Subsequent
logins upsert that record — in particular, admin status is recomputed on
every login when a group-claim mapping is configured (see below), so
promotions/demotions made in your identity provider take effect
immediately, without an admin having to touch Portalcrane's user list.
Custom CA bundle (private PKI)¶
If your OIDC provider is fronted by a private CA (self-signed or internal
PKI), supply the CA chain (intermediate + root, concatenated into a single
PEM file) via the standard SSL_CERT_FILE or REQUESTS_CA_BUNDLE
environment variable, pointing at a mounted file. Outbound OIDC calls then
trust that chain instead of the default certifi bundle. Leave both unset
to keep default verification.
OIDC-only mode & admin bootstrap¶
By default, OIDC is offered alongside local login. Set OIDC_ONLY=true
to disable every local password login — including the built-in
env-admin — and authenticate solely through your provider.
Because there is no break-glass account in this mode, Portalcrane refuses to enable it unless admin access is guaranteed to remain reachable. You need one of:
- Group-claim mapping —
OIDC_ADMIN_GROUP_CLAIM=groupsandOIDC_ADMIN_GROUP=registry-admins(make sure your provider's scope actually exposes that claim). Admin rights are then re-evaluated on every SSO login. - At least one OIDC account already manually promoted to admin through the Accounts panel, if you don't want to rely on a group claim.
The UI enforces this at the API level too — PUT /api/oidc/settings returns
400 if you try to enable oidc_only without one of the above.
Restricting access to specific users (allowlist)¶
By default, every authenticated SSO user is provisioned as a regular user. To restrict access, map regular users the same way as admins:
With OIDC_RESTRICT_TO_GROUPS=true (or Restrict access to mapped
groups ticked in the UI), OIDC access becomes an allowlist: only users
whose claims match the admin or the user mapping may log in. Anyone
matching neither is denied with 403 and their account is not
created. Admin and user group claims may point at different claims from
the token; both are read on every login.
Anti-usurpation protection¶
OIDC identities are kept strictly separate from local accounts to prevent privilege escalation:
- An SSO login is rejected (403) when the resolved username matches the
built-in env-admin username. This stops a provider that returns
preferred_username=adminfrom inheriting the admin account's rights. - An SSO login is also rejected when the username collides with an existing local (password-based) account — an OIDC identity can never bind onto a local account.
- Deleting an OIDC-provisioned account adds its username to a revocation
list (
oidc_revoked.json); the next SSO callback for that username returns403instead of silently re-provisioning it. - A disabled OIDC account (
disabled: true) is rejected at login without being re-enabled.
Deployment scenario: local-only¶
Simplest setup — leave OIDC_ENABLED at its default (false). Only the
admin account plus any local users you create can log in.
Deployment scenario: mixed local + SSO¶
Set OIDC_ENABLED=true with your provider's details, leave OIDC_ONLY
and OIDC_RESTRICT_TO_GROUPS at their defaults. Both a "Sign in with
SSO" button and the local username/password form appear on the login page;
any SSO user gets provisioned as a regular (non-admin) user unless the
admin group mapping says otherwise.
Deployment scenario: SSO-only, group-mapped admins¶
OIDC_ENABLED=true
OIDC_ONLY=true
OIDC_ADMIN_GROUP_CLAIM=groups
OIDC_ADMIN_GROUP=registry-admins
OIDC_USER_GROUP_CLAIM=groups
OIDC_USER_GROUP=registry-users
OIDC_RESTRICT_TO_GROUPS=true
Only members of registry-admins or registry-users (as reported by your
IdP's groups claim) can log in at all; membership in registry-admins
grants admin, re-evaluated on every login. This is the recommended
configuration for centrally managed identity providers (Keycloak, Authentik,
Entra ID, Okta, …).