Skip to main content

Configuring Discourse (identity-only pattern)

Pattern: identity only. Discourse authenticates users through Keycloak but does no group or role mapping. The IdP answers one question, "who is this user," and Discourse handles authorization internally with its own trust levels and groups. Use this pattern for any application that needs single sign-on but manages permissions on its own.

Note: At the time of this writing, Discourse does have the capability for groups to be managed by an IdP, but it uses a Discourse-specific HMAC-signed payload format, not OIDC.

Keycloak side

Create a client in the realm:

Setting Value Why
Client ID discourse Matches the client ID configured in Discourse.
Client authentication On (confidential) Discourse holds a client secret and exchanges the auth code on a back channel.
Standard flow Enabled Authorization-code flow.
PKCE method S256 Proof key for code exchange; hardens the code flow against interception.
Valid redirect URIs https://forum.example.org/auth/oidc/callback Where Keycloak returns the user after login.

No client scopes beyond the defaults are needed. Because Discourse does not consume groups, there is no group-membership mapper and no custom scope to create. This is the whole reason the identity-only pattern is the simplest one: you stop after the client exists.

Discourse side

Discourse's OpenID Connect support is provided by its official connector, configured entirely from the admin settings UI (Admin → Settings → search "openid"). The relevant settings:

Setting Value Notes
openid connect enabled checked Turns on the connector.
openid connect discovery document https://auth.example.org/realms/landisfam/.well-known/openid-configuration Discourse resolves all endpoints from this; you never hand-configure token or authorize URLs.
openid connect client id discourse Must match the Keycloak client ID.
openid connect client secret (from Keycloak, stored in the password manager) The confidential client's secret.
openid connect authorize scope openid email profile The three standard scopes. No groups here, by design.
openid connect match by email checked Links a Keycloak identity to an existing Discourse account by email address.
openid connect use pkce checked Must match the S256 method set on the Keycloak client.

Gotchas

  • Match-by-email is a decision, not a default. Enabling "match by email" means a user who already had a local Discourse account gets linked to their Keycloak identity on first SSO login, rather than getting a second, empty account. Leave it off and you can end up with duplicate accounts. Turn it on only if you trust that email addresses in Keycloak are verified, because email-matching is exactly as trustworthy as your IdP's email verification.
  • PKCE has to agree on both ends. If Keycloak requires S256 and Discourse doesn't send a PKCE challenge (or vice versa), login fails. Set it in both places.

Disabling local login

Once you have confirmed the configuration is working, you can disable local logins and force SSO only.

  • Go to admin -> all site settings -> search for "enable local login."
  • Uncheck "enable local logins" and "enable local logins via email."
  • Then search all site settings for "allow new registrations" and uncheck that as well.

Make sure you have saved your local login credentials somewhere safe, like in a password vault, in case you ever get locked out.

To enable if locked out

If you ever get locked out and need to enable local logins to fix, navigate to the directory where your discourse launcher file sits.

  • Type ./launcher enter app
  • Then rails c
  • Then SiteSetting.enable_local_logins = true

Adapt this for…

Any application that offers "OIDC / OpenID Connect login" as a plugin or built-in feature and handles its own authorization, e.g., forums, wikis without group needs, dashboards, status pages. The recipe is always the same three moves: confidential client in Keycloak, point the app at the discovery document, request openid email profile. If the app never needs to know a user's role from the IdP, you are done here.

If you want to install your own discourse

Discourse has this pretty well documented.