Skip to main content
PathMon

Chapter 6 of 15

Setting Up OIDC SSO

Updated Read the full guide

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 use RS256 by 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 groups claim 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 using HS256, which PatchMon rejects. Every login then fails with a generic "Authentication failed" message on the login page
  • Replace the default email scope 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 groups or ak_groups claim (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 the xms_edov optional 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 reads APP_ENV to determine the runtime environment (e.g. production). NODE_ENV is accepted as a backward-compatibility alias but APP_ENV is 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 oidc is 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

  1. Open PatchMon in your browser
  2. You should see a "Login with SSO" button (or your custom OIDC_BUTTON_TEXT)
  3. Click it and you'll be redirected to your IdP
  4. Authenticate with your IdP credentials
  5. 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:

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:

  1. Set OIDC_AUTO_CREATE_USERS=true
  2. The setup wizard is automatically bypassed when OIDC with auto-create is enabled
  3. 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
  4. 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 picture claim is present
  • First name and last name: from given_name and family_name claims
  • 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:

  1. Sign in to Authentik as an administrator.

  2. Go to Customisation > Property Mappings.

  3. Select Create, then choose Scope Mapping.

  4. 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
      }
      
  5. Select Create.

  6. Open the OAuth2 provider you configured for PatchMon and select Edit.

  7. Expand Advanced protocol settings and scroll to Scopes.

  8. Remove authentik default OAuth Mapping: OpenID 'email'.

  9. Add the mapping you created in step 4.

  10. 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.

  1. In the Microsoft Entra admin centre, open your app registration.
  2. Go to Manage > Token configuration > Add optional claim.
  3. Select token type ID, then add xms_edov. If the portal marks it unrecognised, add it by editing the manifest instead: find the optionalClaims object and add xms_edov to the idToken array.
  4. 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_SCOPES or OIDC_ENABLED is 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 set OIDC_TRUST_UNVERIFIED_EMAIL=true instead. If you configured OIDC in the UI, setting only OIDC_TRUST_UNVERIFIED_EMAIL in 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_LOGGING defaults to true from 2.0.3, but an explicit false in .env or 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_LEVEL is above info. The confirmation line is logged at info level, so warn or error hides 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 config or partially configured in 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 config or partially configured
  • OIDC_ENABLED is not true and 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 as authentik Self-signed Certificate. An empty Signing Key is what causes this
  • The Redirect URI in your IdP does not match OIDC_REDIRECT_URI exactly (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_URL is 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 groups scope is included in OIDC_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 emits groups
  • Group matching is case-insensitive, so patchmon admins matches PatchMon 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). Set APP_ENV to production in your environment. NODE_ENV is 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