Chapter 6 of 15
Setting Up OIDC SSO
Overview
PatchMon supports OpenID Connect (OIDC) authentication, allowing users to log in via an external Identity Provider (IdP) instead of, or in addition to, local username/password credentials.
Supported Providers
Any OIDC-compliant provider works, including:
- Authentik
- Keycloak
- Okta
- Azure AD (Entra ID)
- Google Workspace
What You Get
- SSO login via a configurable button on the login page
- Automatic user provisioning on first login (no need to create accounts manually)
- Group-based role mapping so your IdP controls who is an admin, user, or readonly viewer
- Optional: disable local password login entirely and enforce SSO for all users
Prerequisites
- PatchMon already installed and running
- An OIDC-compatible Identity Provider with an OAuth2/OIDC application configured
- HTTPS in production (OIDC routes enforce HTTPS when
OIDC_ENFORCE_HTTPS=true, which is the default)
Step 1 - Create an OIDC Application in Your IdP
Create a new OAuth2 / OIDC application in your Identity Provider with the following settings:
| Setting | Value |
|---|---|
| Application type | Web application / Confidential client |
| Redirect URI | https://patchmon.example.com/api/v1/auth/oidc/callback |
| Scopes | openid, email, profile, groups |
| Grant type | Authorization Code |
| Token endpoint auth | Client Secret (Basic) |
| ID token signing | An asymmetric algorithm: RS256 (or RS384, RS512, ES256, ES384, ES512, PS256, PS384, PS512, EdDSA) |
After creating the application, note the Client ID and Client Secret as you'll need both.
Important: PatchMon cannot accept HS256-signed ID tokens. PatchMon validates ID tokens against the public keys published at your IdP's JWKS endpoint, so the token must be signed asymmetrically with a certificate or key pair. Symmetric HMAC signing (
HS256,HS384,HS512), where the client secret doubles as the signing key, is rejected. Most identity providers useRS256by default, but Authentik does not unless you tell it to. See the Authentik note below.
Tip: If you plan to use group-based role mapping, ensure your IdP includes the
groupsclaim in the ID token. In Authentik, this is enabled by default. In Keycloak, you may need to add a "Group Membership" mapper to the client scope.
Provider-Specific Notes
Authentik:
- Create an OAuth2/OIDC Provider, then create an Application linked to it
- Set a Signing Key on the provider. This is required and is the single most common cause of a failed Authentik setup. Open the provider, expand Advanced protocol settings, and set Signing Key to a certificate, for example the built-in
authentik Self-signed Certificate. If Signing Key is left empty, Authentik signs ID tokens symmetrically with the client secret usingHS256, which PatchMon rejects. Every login then fails with a generic "Authentication failed" message on the login page - Replace the default
emailscope mapping. Authentik's stock mapping always reports the address as unverified, which PatchMon rejects from 2.1.0 onwards. See The verified email requirement - Issuer URL format:
https://auth.example.com/application/o/patchmon/ - Groups are included via the
groupsorak_groupsclaim (both are supported)
Keycloak:
- Create a Client with Access Type
confidential - Issuer URL format:
https://keycloak.example.com/realms/your-realm - Add a "Group Membership" protocol mapper to include groups in the token
Okta / Azure AD:
- Create an OIDC Web Application
- Ensure groups are included in the ID token claims
- Entra ID only: it does not send
email_verified, which PatchMon requires from 2.1.0 onwards for account linking and auto-creation. Add thexms_edovoptional claim instead. See The verified email requirement
Step 2 - Configure PatchMon
Add the following environment variables to your .env file (for Docker deployments) or your server environment.
Required Variables
OIDC_ENABLED=true
OIDC_ISSUER_URL=https://auth.example.com/application/o/patchmon/
OIDC_CLIENT_ID=your-client-id
OIDC_CLIENT_SECRET=your-client-secret
OIDC_REDIRECT_URI=https://patchmon.example.com/api/v1/auth/oidc/callback
| Variable | Description |
|---|---|
OIDC_ENABLED |
Set to true to enable OIDC |
OIDC_ISSUER_URL |
Your IdP's issuer / discovery URL |
OIDC_CLIENT_ID |
Client ID from your IdP application |
OIDC_CLIENT_SECRET |
Client secret from your IdP application |
OIDC_REDIRECT_URI |
Must match exactly what you configured in your IdP |
Optional Variables
OIDC_SCOPES=openid email profile groups
OIDC_AUTO_CREATE_USERS=false
OIDC_DEFAULT_ROLE=user
OIDC_DISABLE_LOCAL_AUTH=false
OIDC_BUTTON_TEXT=Login with SSO
OIDC_SESSION_TTL=600
OIDC_POST_LOGOUT_URI=https://patchmon.example.com/login
OIDC_ENFORCE_HTTPS=true
OIDC_SYNC_ROLES=false
| Variable | Default | Description |
|---|---|---|
OIDC_SCOPES |
openid email profile groups |
Space-separated scopes to request. Include groups for role mapping |
OIDC_AUTO_CREATE_USERS |
false |
When true, automatically creates a PatchMon account on first OIDC login. When false, the user must already exist in PatchMon (matched by email) |
OIDC_DEFAULT_ROLE |
user |
Role assigned when a user doesn't match any group mapping |
OIDC_DISABLE_LOCAL_AUTH |
false |
When true, hides the username/password fields and only shows the SSO button |
OIDC_BUTTON_TEXT |
Login with SSO |
Label shown on the SSO login button |
OIDC_SESSION_TTL |
600 |
Seconds the OIDC login state is valid. If the user takes longer than this at the IdP, the session expires and they must try again |
OIDC_POST_LOGOUT_URI |
<CORS_ORIGIN>/login |
Where to redirect after a logout. Defaults to the PatchMon login page |
OIDC_ENFORCE_HTTPS |
true |
When true, enforces HTTPS on OIDC login and callback routes. Set to false only for local development |
OIDC_TRUST_UNVERIFIED_EMAIL |
false |
When true, waives the verified-email requirement for account linking and auto-creation. Reduces account-takeover protection |
OIDC_SYNC_ROLES |
false |
When true, the user's role is updated on every login based on current group membership. When false, roles are managed locally in PatchMon and OIDC login does not change them |
Note on
APP_ENV: PatchMon readsAPP_ENVto determine the runtime environment (e.g.production).NODE_ENVis accepted as a backward-compatibility alias butAPP_ENVis preferred.
Step 3 - Group-Based Role Mapping (Optional)
Map your IdP groups to PatchMon roles so that role assignments stay in sync with your directory. Group matching is case-insensitive.
Role Hierarchy
PatchMon checks group membership in this order (highest priority first):
| PatchMon Role | Required IdP Group(s) | Description |
|---|---|---|
| Super Admin | Member of OIDC_SUPERADMIN_GROUP |
Full access including managing other superadmins |
| Admin | Member of OIDC_ADMIN_GROUP |
Full access |
| Host Manager | Member of OIDC_HOST_MANAGER_GROUP |
Manage hosts and groups |
| User | Member of OIDC_USER_GROUP |
Standard access with data export |
| Readonly | Member of OIDC_READONLY_GROUP |
View-only access |
| Default | None of the above | Gets OIDC_DEFAULT_ROLE (defaults to user) |
Priority: If a user is in multiple groups, the highest-priority role wins. The priority order from highest to lowest is: Super Admin > Admin > Host Manager > Readonly > User.
Environment Variables
OIDC_ADMIN_GROUP=PatchMon Admins
OIDC_USER_GROUP=PatchMon Users
OIDC_SUPERADMIN_GROUP=PatchMon SuperAdmins
OIDC_HOST_MANAGER_GROUP=PatchMon Host Managers
OIDC_READONLY_GROUP=PatchMon Readonly
OIDC_SYNC_ROLES=true
| Variable | Description |
|---|---|
OIDC_ADMIN_GROUP |
IdP group name that maps to Admin role |
OIDC_USER_GROUP |
IdP group name that maps to User role |
OIDC_SUPERADMIN_GROUP |
IdP group name that maps to Super Admin |
OIDC_HOST_MANAGER_GROUP |
IdP group name that maps to Host Manager role |
OIDC_READONLY_GROUP |
IdP group name that maps to Readonly role |
OIDC_SYNC_ROLES |
When true, the user's role is updated on every login based on current group membership. When false (default), roles are managed locally in PatchMon and OIDC login does not change them |
You only need to define the groups you intend to use. Any variables left unset are simply ignored.
Step 4 - Restart PatchMon
After updating your .env file, restart the server so it picks up your OIDC configuration:
# Docker
docker compose restart patchmon-server
# Or if rebuilding
docker compose up -d --force-recreate patchmon-server
# Native systemd installation
sudo systemctl restart <your-domain>
This check needs ENABLE_LOGGING=true, which is the default from 2.0.3. If you have set ENABLE_LOGGING=false, or you are on 2.0.2 or earlier where false was the default, the server writes no logs at all and the commands below return nothing regardless of whether OIDC loaded. Keep LOG_LEVEL at info or lower too, since the confirmation line is logged at info level.
# Docker
docker compose logs patchmon-server | grep -i oidc
# Native systemd
journalctl -u <your-domain> | grep -i oidc
You should see a line confirming SSO is enabled, containing the message OIDC SSO enabled; provider discovery is deferred to the first login attempt along with the issuer and client ID that were loaded.
PatchMon does not contact your identity provider at startup. Provider discovery (the request to .well-known/openid-configuration) is deliberately deferred until the first login attempt, so a temporarily unreachable IdP cannot stop the server from booting. This line therefore confirms only that your four required variables were read and that the SSO button will appear. It does not prove your IdP is reachable or that your provider is configured correctly. The first login attempt is what tests that, and any failure is logged at that point.
The surrounding format depends on APP_ENV. In production, which is the default, logs are JSON:
{"time":"2026-08-12T20:34:34.526Z","level":"INFO","msg":"OIDC SSO enabled; provider discovery is deferred to the first login attempt","version":"2.0.3","port":3000,"issuer":"https://auth.example.com/application/o/patchmon/","client_id":"patchmon","source":"environment variables"}
With APP_ENV set to anything else, the same record is printed as plain text:
time=2026-08-12T20:34:34.526Z level=INFO msg="OIDC SSO enabled; provider discovery is deferred to the first login attempt" version=2.0.3 port=3000 issuer=https://auth.example.com/application/o/patchmon/ client_id=patchmon source="environment variables"
The source field tells you which configuration won: environment variables or database settings. If you edited SSO settings in the web UI but see environment variables, your .env is overriding them.
If you see OIDC is enabled but missing required config: ..., one or more of the four required variables is empty. If you see OIDC is partially configured via env vars but SSO is disabled, you have set some but not all of them.
Note: Releases before 2.0.3 logged nothing at all on a successful OIDC configuration. If you are on 2.0.2 or older, an empty
grep -i oidcis expected and does not mean OIDC failed to load. Check whether the SSO button appears on the login page instead.
Step 5 - Test the Login
- Open PatchMon in your browser
- You should see a "Login with SSO" button (or your custom
OIDC_BUTTON_TEXT) - Click it and you'll be redirected to your IdP
- Authenticate with your IdP credentials
- You'll be redirected back to PatchMon and logged in
If OIDC_AUTO_CREATE_USERS is true, a PatchMon account is created automatically using your email address. The username is derived from the email prefix (e.g. john.doe@example.com becomes john.doe).
First-Time Setup (No Users Exist Yet)
When PatchMon has no users in the database, it displays a setup wizard. You have two options:
Option A - Use the Setup Wizard (Recommended)
Complete the setup wizard to create your first admin account. This account is created as a Super Admin with full access. You can then enable OIDC from Settings afterwards.
Option B - Log In via OIDC Directly
If you've pre-configured OIDC via environment variables before first boot:
- Set
OIDC_AUTO_CREATE_USERS=true - The setup wizard is automatically bypassed when OIDC with auto-create is enabled
- The first user to log in via OIDC is automatically promoted to Super Admin, regardless of group mapping, to ensure the system always has an admin
- Subsequent OIDC users get roles based on group mapping or the default role as normal
Note: You do not need to configure group mapping for the first user. The auto-promotion happens because PatchMon detects no admin exists yet.
What Syncs from Your IdP
On every OIDC login, PatchMon automatically syncs the following from your Identity Provider:
- Avatar / profile picture: synced if the
pictureclaim is present - First name and last name: from
given_nameandfamily_nameclaims - Email: used for matching and account linking
The following is only synced when OIDC_SYNC_ROLES=true:
- Role: based on group membership. When sync is off, roles are managed locally in PatchMon and OIDC login does not change them. You can use OIDC for authentication while still managing roles manually.
Account Linking
If a local PatchMon user already exists with the same email as the OIDC user, PatchMon will automatically link the accounts, but only if the IdP marks that email as verified. See The verified email requirement below, which applies to new accounts too, not only to linking.
The Verified Email Requirement
Applies from PatchMon 2.1.0.
When PatchMon cannot recognise you by a subject it has already stored, the email address is the only thing deciding who you are. Trusting an unverified one would let anyone who can set their own email address at your IdP sign in as an existing PatchMon user. So in that situation PatchMon requires the IdP to state that the address is verified, by sending an email_verified claim set to true.
This applies in two cases:
- Linking to an existing local account by matching email.
- Creating a new account on first login when
OIDC_AUTO_CREATE_USERS=true.
It does not apply once an account is linked. After a successful first login PatchMon stores the IdP's subject identifier, matches on that from then on, and the email claim stops being the deciding factor.
If the claim is missing, PatchMon treats it as not verified. Several identity providers do not send it by default, and two common ones need configuration:
Authentik
Authentik's stock authentik default OAuth Mapping: OpenID 'email' scope mapping returns email_verified: False unconditionally, because Authentik does not track per-user email verification. Every login therefore fails until you replace that mapping:
-
Sign in to Authentik as an administrator.
-
Go to Customisation > Property Mappings.
-
Select Create, then choose Scope Mapping.
-
Fill it in as follows:
-
Name:
authentik main OAuth Mapping: OpenID verified 'email' -
Scope name:
email -
Description:
Verified Email address -
Expression:
return { "email": request.user.email, "email_verified": True }
-
-
Select Create.
-
Open the OAuth2 provider you configured for PatchMon and select Edit.
-
Expand Advanced protocol settings and scroll to Scopes.
-
Remove
authentik default OAuth Mapping: OpenID 'email'. -
Add the mapping you created in step 4.
-
Sign in to PatchMon again.
By making this change you are asserting that the email addresses in your Authentik directory are trustworthy. That is a reasonable statement for a directory you control and populate yourself. It is not reasonable if users can self-register with an arbitrary address.
Microsoft Entra ID
Entra ID does not send email_verified at all, and there is no way to make it. Adding email as an optional claim does not help, because that supplies the address and not the verification signal.
Instead, from PatchMon 2.1.2, add the xms_edov optional claim. This is Microsoft's own "Email Domain Owner Verified" signal, introduced in response to nOAuth, which is the same account-takeover-by-email-spoofing attack this requirement exists to prevent. PatchMon reads it when email_verified is absent, so no security setting has to be relaxed.
- In the Microsoft Entra admin centre, open your app registration.
- Go to Manage > Token configuration > Add optional claim.
- Select token type ID, then add
xms_edov. If the portal marks it unrecognised, add it by editing the manifest instead: find theoptionalClaimsobject and addxms_edovto theidTokenarray. - Sign in to PatchMon again.
On older PatchMon versions, or if you would rather not configure the claim, leave OIDC_AUTO_CREATE_USERS off and link accounts by having each user sign in once while a matching local account exists, or use the opt-in below.
If your provider cannot assert verification at all
Available from PatchMon 2.1.2.
Some directories genuinely have no notion of a verified email address. For those, set Trust unverified email in Settings > OIDC, or OIDC_TRUST_UNVERIFIED_EMAIL=true in the environment. It is off by default.
Set it in the same place you configured OIDC itself. PatchMon takes its whole OIDC configuration from one source or the other, never a mix: if any of
OIDC_ISSUER_URL,OIDC_CLIENT_ID,OIDC_CLIENT_SECRET,OIDC_REDIRECT_URI,OIDC_SCOPESorOIDC_ENABLEDis set in the environment, every OIDC setting comes from the environment and the values in Settings > OIDC are ignored. So if you configured OIDC through environment variables, the toggle in the UI will appear to save but have no effect, and you must setOIDC_TRUST_UNVERIFIED_EMAIL=trueinstead. If you configured OIDC in the UI, setting onlyOIDC_TRUST_UNVERIFIED_EMAILin the environment does nothing, and you must use the toggle. This applies to every OIDC setting, not just this one.
Be clear about what this does. With it on, anyone who can set their own email address at your identity provider can sign in as an existing PatchMon user with that address. It is only reasonable when you control who can change addresses in your directory. Every login it permits is recorded in the server log at warn level, so a relaxed deployment stays visible:
oidc accepting unverified email: trust_unverified_email is enabled
Prefer a real fix where one exists: the Authentik scope mapping above, or xms_edov for Entra.
Other providers
Keycloak, Okta and Google Workspace all send email_verified correctly with their default configuration and need no change.
To check what your IdP actually sends, decode the ID token at jwt.io after a login attempt, or read the rejection in the server log (see Troubleshooting).
Disabling Local Authentication
To enforce SSO for all users, set:
OIDC_DISABLE_LOCAL_AUTH=true
This hides the username/password fields on the login page and only shows the SSO button. Local authentication is only actually disabled if OIDC is also enabled and successfully initialised. This safety check prevents you from being locked out if OIDC is misconfigured.
Important: Ensure at least one OIDC user has admin access before enabling this, or you may lose the ability to manage PatchMon.
Complete Example Configuration
Authentik
# .env
APP_ENV=production
OIDC_ENABLED=true
OIDC_ISSUER_URL=https://authentik.example.com/application/o/patchmon/
OIDC_CLIENT_ID=patchmon
OIDC_CLIENT_SECRET=your-client-secret-here
OIDC_REDIRECT_URI=https://patchmon.example.com/api/v1/auth/oidc/callback
OIDC_SCOPES=openid email profile groups
OIDC_AUTO_CREATE_USERS=true
OIDC_DEFAULT_ROLE=user
OIDC_BUTTON_TEXT=Login with Authentik
OIDC_ADMIN_GROUP=PatchMon Admins
OIDC_USER_GROUP=PatchMon Users
OIDC_SYNC_ROLES=true
Reminder: On the Authentik side, the provider's Signing Key (under Advanced protocol settings) must be set to a certificate. Leaving it empty makes Authentik sign ID tokens with
HS256, which PatchMon rejects, and every login will fail with "Authentication failed".
Keycloak
# .env
APP_ENV=production
OIDC_ENABLED=true
OIDC_ISSUER_URL=https://keycloak.example.com/realms/your-realm
OIDC_CLIENT_ID=patchmon
OIDC_CLIENT_SECRET=your-client-secret-here
OIDC_REDIRECT_URI=https://patchmon.example.com/api/v1/auth/oidc/callback
OIDC_SCOPES=openid email profile groups
OIDC_AUTO_CREATE_USERS=true
OIDC_DEFAULT_ROLE=user
OIDC_BUTTON_TEXT=Login with Keycloak
OIDC_ADMIN_GROUP=PatchMon Admins
OIDC_USER_GROUP=PatchMon Users
OIDC_SYNC_ROLES=true
Troubleshooting
OIDC Not Initialising
Logs show: OIDC is enabled but missing required config: ...
All four required variables must be set: OIDC_ISSUER_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_REDIRECT_URI. Check for typos or empty values. A related message, OIDC is partially configured via env vars but SSO is disabled, means some but not all of the four are set.
Login Fails with "no email in UserInfo or id_token"
Logs show: oidc exchange failed with error: oidc: no email in UserInfo or id_token, whilst the login page shows a generic "Authentication failed".
Fixed in 2.1.3. Two separate defects produced the same message. ADFS sends single-valued claims as a JSON array ("email": ["user@example.com"]) in the ID token, and PatchMon only accepted a plain string. Separately, any provider whose UserInfo endpoint returns no email caused that empty value to hide a perfectly good email in the ID token. PatchMon now accepts the array form in the ID token and treats an empty value as missing, so it falls through correctly.
If you are on 2.1.3 or later and still see this, your provider is genuinely not sending an email address. Check that the email scope is requested and that the claim is mapped on the provider side, then decode the ID token at jwt.io after a login attempt to confirm what arrives. PatchMon reads only the standard email claim; there is no claim mapping setting, and upn is not used as a substitute.
A related but distinct failure logs oidc: fetch userinfo: oidc: failed to decode userinfo instead. That means your provider returned an array (or another non-string type) for a field in its UserInfo response rather than in the ID token. That response is decoded before PatchMon sees it, so the array handling above does not apply. Have the provider return plain strings from /userinfo, or map the claim into the ID token instead.
No OIDC Lines in the Startup Logs
On a healthy configuration, PatchMon 2.0.3 and later logs OIDC SSO enabled; provider discovery is deferred to the first login attempt at startup. It does not contact your identity provider at startup, so there are never any discovery or connection messages to look for.
An empty grep -i oidc has four possible causes, and only the last one is a problem with your SSO configuration:
- Logging is off.
ENABLE_LOGGINGdefaults totruefrom 2.0.3, but an explicitfalsein.envor Settings > Environment is still honoured, and it was the default on earlier releases. With logging disabled the server writes nothing at all, so this check tells you nothing LOG_LEVELis aboveinfo. The confirmation line is logged at info level, sowarnorerrorhides it- You are on 2.0.2 or older. A correct configuration logged nothing at all on those releases
- Your configuration did not resolve. Check for
missing required configorpartially configuredin the same output
If logging is off and you would rather not turn it on, check whether the SSO button appears on the login page instead. That is driven by the same resolved configuration.
You may also see a second OIDC line warning that role sync cannot grant superadmin. That is unrelated to whether SSO loaded and is covered under Step 3.
SSO Button Not Appearing
The button appears when all four required variables resolve to non-empty values (from environment variables, or from Settings in the web UI). Because PatchMon does not contact your IdP until someone actually logs in, an unreachable or misconfigured IdP does not hide the button. If the button is missing, the problem is in your configuration values rather than your IdP:
- One of the four required variables is empty or misspelled. Check the startup logs for
missing required configorpartially configured OIDC_ENABLEDis nottrueand no OIDC settings have been saved in the web UI- The client secret was saved in the web UI but cannot be decrypted, which is logged as
OIDC client secret could not be decrypted; treating OIDC as unconfigured. This usually means your encryption key changed. Re-enter and save the secret
"Authentication Failed" After Redirect
This is the generic error for any failure during the token exchange, after your IdP has sent the user back to PatchMon. Check the server logs for the line beginning oidc exchange failed, which contains the specific reason. Common causes:
- Your IdP is signing ID tokens with HS256. The log contains
unexpected signature algorithm "HS256". PatchMon only accepts asymmetrically signed tokens. In Authentik, open the OAuth2/OIDC provider, expand Advanced protocol settings, and set Signing Key to a certificate such asauthentik Self-signed Certificate. An empty Signing Key is what causes this - The Redirect URI in your IdP does not match
OIDC_REDIRECT_URIexactly (including trailing slashes) - Cookies are being blocked (OIDC uses httpOnly cookies for session state)
- Your IdP does not support PKCE (PatchMon uses the S256 code challenge)
"Unable to sign in with this account" After a Successful IdP Login
You signed in at your IdP, it sent you back, and PatchMon refused you. The token exchange worked, so this is not the error above. Check the server logs, where one of these lines gives the reason:
| Log line | Meaning | Fix |
|---|---|---|
oidc login rejected: unverified email claim |
Your IdP sent neither email_verified nor xms_edov. From 2.1.0 PatchMon requires one of these when it has to identify you by email. Affects Authentik and Microsoft Entra ID out of the box |
The verified email requirement |
oidc accepting unverified email: trust_unverified_email is enabled |
Not an error. Records that a login was allowed through with an unverified address because the opt-in is on | Expected if you enabled it deliberately. If not, turn it off in Settings > OIDC |
oidc user not found and auto-create disabled |
No PatchMon account matches, and OIDC_AUTO_CREATE_USERS is off |
Create the user in PatchMon first, or set OIDC_AUTO_CREATE_USERS=true |
A deactivated account is a different case and shows "Account disabled" rather than this message, logged as oidc login inactive user.
If your logs show nothing at all, raise LOG_LEVEL to debug and try again.
"Failed to reach the OIDC provider" When Clicking the SSO Button
You get this immediately on clicking the SSO button, before your IdP's login page ever appears. The browser shows it as a bare JSON response rather than a styled error page, and the logs show oidc auth url failed.
PatchMon fetches your IdP's discovery document (.well-known/openid-configuration) on the first login attempt rather than at startup, so problems reaching or validating that document surface at this point:
- PatchMon cannot reach the IdP from inside the container (DNS, firewall, or network policy)
OIDC_ISSUER_URLis wrong. It must not include.well-known/openid-configuration, which PatchMon appends itself- The issuer URL in the discovery document does not match
OIDC_ISSUER_URL. For Authentik, the trailing slash matters - The IdP's TLS certificate is not trusted by the PatchMon container
"Session Expired" Error
The OIDC login state has a configurable window (default 600 seconds via OIDC_SESSION_TTL). If the user takes longer than this at the IdP, the session expires. Simply try logging in again, or increase OIDC_SESSION_TTL if this is happening frequently.
User Gets Wrong Role
- Check that the
groupsscope is included inOIDC_SCOPES - Verify your IdP is including groups in the ID token (not just the access token)
- If the logs show
oidc no groups in token, your IdP sent no groups at all. Configure it to include the groups claim. In Authentik this means adding a Scope Mapping that emitsgroups - Group matching is case-insensitive, so
patchmon adminsmatchesPatchMon Admins
OIDC Banners / Restrictions Appearing When They Shouldn't
If you see "OIDC Authentication Enabled" banners on the Users or Roles settings pages, or the "Add User" / "Add Role" buttons are missing, OIDC_SYNC_ROLES is enabled. These restrictions only apply when role sync is active. If you want to use OIDC for login but manage roles locally, set OIDC_SYNC_ROLES=false (or leave it unset; it defaults to false).
"User Not Found" Error
OIDC_AUTO_CREATE_USERS is false (the default) and no matching PatchMon account exists. Either set OIDC_AUTO_CREATE_USERS=true or create the user account manually in PatchMon first (the email must match).
Debug Logging
For detailed OIDC troubleshooting, enable debug logging:
LOG_LEVEL=debug
Then check the server logs:
# Docker
docker compose logs -f patchmon-server | grep -i oidc
# Native systemd
journalctl -u <your-domain> -f | grep -i oidc
Security Notes
- HTTPS is enforced for OIDC login and callback routes when
OIDC_ENFORCE_HTTPS=true(the default). SetAPP_ENVtoproductionin your environment.NODE_ENVis also accepted as a backward-compatibility alias - PKCE (S256) is used for all authorization code exchanges
- Tokens are stored in httpOnly cookies, not localStorage, to prevent XSS attacks
- Client secrets should never be committed to version control
- Account linking only occurs when the IdP reports the email as verified
- Role sync can be disabled (
OIDC_SYNC_ROLES=false, which is the default) if you prefer to manage roles manually in PatchMon after first login