Discourse OpenID Connect

:discourse2: Summary Discourse OpenID Connect allows an OpenID Connect provider to be used as an authentication provider for Discourse.
:hammer_and_wrench: Repository Link https://github.com/discourse/discourse-openid-connect
:open_book: Install Guide How to install plugins in Discourse

Features

The plugin aims to provide a minimal implementation of the specification. Specifically, it supports the “Authorization Code Flow”. To get started, follow the plugin installation instructions, or contact your hosting provider.

Our oauth2-basic plugin can be used for connecting to some openid-connect providers (OpenID Connect is based on OAuth2). However, this plugin should require far less manual configuration, and can make use of the JWT “ID Token” if a JSON API is not available.

Configuration is automatically performed using an OpenID Connect Discovery Document. According to the specification, this should be located at <issuer domain>/.well-known/openid-configuration, but Discourse supports any path to allow for non-compliant implementations (e.g. Azure B2C). The discovery document is cached for 10 minutes, to improve performance on high-traffic sites.

If the discovery document includes a userinfo_endpoint parameter, then the plugin will use that to collect user metadata. If not, the plugin will extract metadata from the id_token (A JWT) supplied by the token endpoint. The plugin DOES NOT verify the authenticity of the JWT signature, as this would significantly increase complexity. This decision is supported by the specification:

If the ID Token is received via direct communication between the Client and the Token Endpoint (which it is in this flow), the TLS server validation MAY be used to validate the issuer in place of checking the token signature.

Configuration

Basic Configuration Options

  • openid_connect_enabled: Enable OpenID Connect authentication

  • openid_connect_discovery_document: OpenID Connect discovery document URL. Normally located at https://your.domain/.well-known/openid-configuration

  • openid_connect_client_id: OpenID Connect client ID

  • openid_connect_client_secret: OpenID Connect client secret

  • openid connect rp initiated logout: Redirect the user to end_session_endpoint after logout. Must be supported by your identity provider and included in the discovery document.

  • openid connect rp initiated logout redirect: (optional) The post_logout_redirect_uri which will be passed to the logout endpoint. If provided, it must be registered with the identity provider.

  • openid_connect_authorize_scope: The scopes sent to the authorize endpoint. This must include ‘openid’

  • openid_connect_verbose_logging: Log detailed openid-connect authentication information to /logs. Keep this disabled during normal use.

Advanced Configuration Options

  • openid_connect_token_scope: The scopes sent when requesting the token endpoint. The official specification does not require this.

  • openid_connect_error_redirects: If the callback error_reason contains the first parameter, the user will be redirected to the URL in the second parameter. Used for unusual implementations that send errors in response to user input (e.g. Azure B2C)

  • openid_connect_allow_association_change: Allow users to disconnect and reconnect their Discourse accounts from the OpenID Connect provider

Example setup

Here we will set up the openid-connect plugin to connect to Google’s OpenID Connect provider. This replicates functionality that already exists in the core of Discourse, but it serves as an accessible example.

  1. Head to OpenID Connect  |  Authentication  |  Google for Developers and follow the instructions to obtain OAuth Credentials.

  2. On the same page, follow the instructions to add a redirect URI. This should be https://<your_forum>/auth/oidc/callback (without a trailing slash)

  3. Go to your Discourse site settings and search for “openid_connect”

    • openid connect enabled:

    • openid connect discovery document: https://accounts.google.com/.well-known/openid-configuration

    • openid connect client id: <client-id>

    • openid connect client secret: <client-secret>

    • openid connect authorize scope: openid email (with a space in between)

  4. You’re done. The “Login with OpenID Connect” button will now log in using Google :tada:. These same steps can be applied to other providers, with very minimal changes.

Debugging

In addition to the verbose_logging setting described above, you can access data about OIDC associations using the data-explorer plugin:

SELECT user_id, provider_name, provider_uid
FROM user_associated_accounts
WHERE provider_name = 'oidc'

Or on the rails console:

User.find_by_username("david").user_associated_accounts.where(provider_name: 'oidc')

Provider Specific Notes

Please feel free to update this if you find any provider-specific quirks relating to this integration:

Azure AD

Add the email scope, and make sure you’re using the version 2 endpoint configuration document. For example

https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration
Azure B2C

The discovery document URL details can be found here: Web sign in with OpenID Connect - Azure Active Directory B2C | Microsoft Learn

To make emails work:

Yahoo
  1. Head to Yahoo and create a new app

  2. Enter the Application Name, and set the callback domain to your forum domain (e.g. meta.discourse.org)

  3. Under API Permissions, choose Profiles: Read/Write Public and Private. This is the only way I know of to obtain the user email address

  4. Save the app

  5. In the Discourse OIDC settings, set the discovery document to

    https://login.yahoo.com/.well-known/openid-configuration
    
  6. Enter the client ID and secret from Yahoo

  7. Enable the OIDC plugin

AWS Cognito
  1. Go to Cognito and select or create a new user pool.
  2. Define an app in App clients.
  3. Leave everything to default, but change Auth Flows Configuration to only select ALLOW_REFRESH_TOKEN_AUTH.
  4. Go to app client settings and select the new app.
  5. Change the callback URL to https://yoursite.example.com/auth/oidc/callback.
  6. Only check the Authorization code grant flow among “Allowed OAuth Flows”.
  7. Check all scopes needed (I have all checked).
Okta
  1. Configure Discourse with your Okta app client ID and secret

  2. Set the discovery document URL to

    https://{your-app}.okta.com/.well-known/openid-configuration
    
  3. In Discourse, set the openid connect authorize scope to openid email

:discourse2: Hosted by us? This plugin is available on our Business and Enterprise plans. OAuth 2.0 & OpenID Connect Support | Discourse - Civilized Discussion

45 Likes

Hi there,
we are using the OpenID Connect Authentication Plugin with a Discourse installation on AWS.
We have deployed the containers Discourse, Discourse Sidekiq and Redis (Based on Bitnami but please don’t kick me out ;). The DB is running on AWS RDS. We use KeyCloak.

Things do run.

But sometimes after a restart of the Discourse AWS Task it happens that it thinks that it has the Discovery document in the cache but then there is no doc there. And it does not try to re-retrieve it from KeyCloak:

OIDC Log: Discovery document loaded from cache
OIDC Log: Discovery document is
---
(oidc) Request phase initiated.
(oidc) Authentication failure! openid_connect_discovery_error: OmniAuth::OpenIDConnect::DiscoveryError, Discovery document is missing

In the browser app I see: Unable to fetch configuration from identity provider. Please try again.

What can you advise ?

1 Like

Is there a way to handle identity management from the idP? This is great for SSO, and JIT. I would like to take deprovisioning steps based on identity state. Normally, this would be a SCIM integration, not sure if Discourse supports SCIM

1 Like

Do you mean Okta? There are settings in the OP. (There are way too many TLAs in your post btw :D)

Sorry for that :frowning: I am looking for a way to manage the internal identities (employees) on our community board. Main reason is for the timely suspension of accounts that are no longer working for the company.

With SCIM (Standard for Cross domain Identity Management) it will update the user to “Suspended” if the identity in Okta (or any identity provider) is deactivated. OpenID Connect or SAML doesn’t support that irc. This is likely not the right thread to ask this question though.

1 Like

Hi @danibe i am facing exactly the same issue. Would you be kind to advise what “missing settings in auth part” i might be missing?

thanks

Found the issue: email_verified was found false and fixed on the Keycloak side :see_no_evil:

@david Does above steps works with User flows in Azure B2C, which are pre-built policy for use cases.

Email address doesn’t get populated in the new user dialog when the authorization is returned. Using a standard user flow to manage the sign-up experience, but the claim is returning something labeled as “emails” and it’s a stringcollection, not a single email.

Setting up openid_connect_authorize_scope to openid emails also doesn’t work.

This plugin is built to work with standards-compliant OIDC services. That means the email should be returned in an “email” field.

If you check the “Azure B2C” section in the first post of this topic, there are some community-provided instructions for setting up a “custom policy” which puts the email address in a standards-compliant location. I’m afraid I’m not familiar enough with Azure B2C to know whether that will work with the “user flows” feature.

1 Like

Thank you @david, it works with custom policy, was hoping to go with much easier route of User flows, but that is out of our control to change.

Few of the suggestions for plugin:

  • Both Sign and Signup button redirect user to login page, in Azure B2C different page can be shown by appending parameter
1 Like

Hello,

Is there a way to set the discourse user avatar source to a field which is specified in the openID service?

Edit: we’re using keycloak

Hello,

I have a requirement similar to @TomĂĄĹĄ_Guba one : I would like to get value from a custom entry in user profile and use it in a [custom] user field.

In my personal case, i have a discovery document with a userinfo_endpoint

Is there something like that in the plugin roadmap ?

Thanks

Hi,

Is it intended that a user is still able to login with the local password? Let’s say a user creates an account via oidc login. At this point everything is fine, the user can log in via the oidc provider and the account is protected via 2fa enforcement on the oidc provider as well.

Now, the user set a local password for this oidc connected account via the password reset email feature. After setting the password, login is possible using the local password and oidc, but the local login is not 2fa protected and potentially insecure. To make it even worse there seems to be no way back, after setting a local password users can’t remove it again, and they can also not setup 2fa because this will disable social logins. I would like to have an option to disallow local logins for oidc users and to be even more strict an option to disallow all other social logins as well to make oidc login mandatory for oidc connected accounts.

Thanks.

Just to check - did you toggle the enable local logins admin setting?

That will disable local login for everyone, not what I want :slight_smile:

Usecase:

  • Mandatory login via oidc for employees
  • Community login via social login or local user registration
1 Like

it should be openid email

how can add 2 or more discovery documents. i already added for google and now i need to add for yahoo.

this worked for me

https://api.login.yahoo.com/.well-known/openid-configuration

Hello, I managed to make the plugin work with my SSO openID but it doesn’t come filled in the username field of the other system or email among other fields…

I imagine that I should configure something in the “openid connect claims” field, but I don’t know how to configure this field directly. Can someone give me an example? Here are some prints of how my project is:

I tried it still facing an issue, could you also share what kind of configuration is to be used on the Okta side also.

Thanks,
Gowthamraj