Discourse OAuth2 de base

:discourse2: Summary Discourse OAuth2 Basic supports basic OAuth2 providers, assuming they have a JSON API endpoint where user details can be retrieved by token.
:open_book: Install Guide This plugin is bundled with Discourse core. There is no need to install the plugin separately.

Features

This plugin allows you to use a basic OAuth2 provider as authentication for Discourse. It should work with many providers, with the caveat that they must provide a JSON endpoint for retrieving information about the user you are logging in.

This is mainly useful for people who are using login providers that aren’t very popular. If you want to use Google, Facebook or Twitter, those are included out of the box and you don’t need this plugin. You can also look for other login providers in our Github Repo.

Configuration

Basic Configuration

  1. First, register your Discourse application with your OAuth2 provider. It will require a Redirect URI which will be:

    http://DISCOURSE_HOST/auth/oauth2_basic/callback

:information_source: Replace DISCOURSE_HOST with the appropriate value, and make sure you are using https if enabled. The OAuth2 provider should supply you with a client ID and secret, as well as a couple of URLs.

  1. Visit your AdminSettingsOAuth2 Login and fill in the basic configuration for the OAuth2 provider:
  • oauth2_enabled - check this off to enable the feature
  • oauth2_client_id - the client ID from your provider
  • oauth2_client_secret - the client secret from your provider
  • oauth2_authorize_url - your provider’s authorization URL
  • oauth2_token_url - your provider’s token URL.

:information_source: If you can’t figure out the values for the above settings, check the developer documentation from your provider or contact their customer support.

Configuring the JSON User Endpoint

Discourse is now capable of receiving an authorization token from your OAuth2 provider. Unfortunately, Discourse requires more information to be able to complete the authentication.

We require an API endpoint that can be contacted to retrieve information about the user based on the token.

For example, the OAuth2 provider SoundCloud provides such a URL. If you have an OAuth2 token for SoundCloud, you can make a GET request to https://api.soundcloud.com/me?oauth_token=A_VALID_TOKEN and will get back a JSON object containing information on the user.

To configure this on Discourse, we need to set the value of the oauth2_user_json_url setting. In this case, we’ll input the value of:

https://api.soundcloud.com/me?oauth_token=:token

The part with :token tells Discourse that it needs to replace that value with the authorization token it received when the authentication completed.

There is one last step to complete. We need to tell Discourse what attributes are available in the JSON it received. Here’s a sample response from SoundCloud:

{
  "id": 3207,
  "permalink": "jwagener",
  "username": "Johannes Wagener",
  "uri": "https://api.soundcloud.com/users/3207",
  "permalink_url": "http://soundcloud.com/jwagener",
  "avatar_url": "http://i1.sndcdn.com/avatars-000001552142-pbw8yd-large.jpg?142a848",
  "country": "Germany",
  "full_name": "Johannes Wagener",
  "city": "Berlin"
}

The oauth2_json_user_id_path, oauth2_json_username_path, oauth2_json_name_path and oauth2_json_email_path variables should be set to point to the appropriate attributes in the JSON.

The only mandatory attribute is id - we need that so when the user logs on in the future that we can pull up the correct account. The others are great if available – they will make the signup process faster for the user as they will be pre-populated in the form.

Here’s how I configured the JSON path settings:

  oauth2_json_user_id_path: 'id'
  oauth2_json_username_path: 'permalink'
  oauth2_json_name_path: 'full_name'

I used permalink because it seems more similar to what Discourse expects for a username than the username in their JSON. Notice I omitted the email path: SoundCloud do not provide an email so the user will have to provide and verify this when they sign up the first time on Discourse.

If the properties you want from your JSON object are nested, you can use periods. So for example if the API returned a different structure like this:

{
  "user": {
    "id": 1234,
    "email": {
      "address": "test@example.com"
    }
  }
}

You could use user.id for the oauth2_json_user_id_path and user.email.address for oauth2_json_email_path.

If the key itself includes periods, you will need to put double quotes around it, or escape the periods with a backslash. For example, given this JSON:

{
  "example.com/uid": "myuid"
}

You would specify the path as example\.com/uid or "example.com/uid"

:warning: If you set oauth2_json_email_path, the OAuth2 provider must confirm the user owns that email address. Failure to do this can result in account takeover in Discourse!

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

Last edited by @tobiaseigen 2025-07-16T21:39:12Z

Check documentPerform check on document:
28 « J'aime »

Bonjour,
Nous essayons d’intégrer Discourse à notre application en utilisant OAuth2 Basic mais nous rencontrons l’erreur suivante dans les logs :
Note : Nous utilisons NGROK pour déboguer la connexion.

OAuth2 Debugging: request POST https://formshare.ngrok.io/oauth2/token

Headers: {"User-Agent"=>"Faraday v1.9.3", "Content-Type"=>"application/x-www-form-urlencoded", "Authorization"=>"Basic S2k2SFZtTVpuSTFHUExiRXVlWVJDN4CbOkNvb1k0anlQemt3dWNRV21Sa2FWOVNnbHZLbjJFT3cxc3BIMmtMck9yY21vNDM4Tg=="}

Body: {"client_id"=>"Ki6HVmMZnI1GPLbEueYRC4Cb", "client_secret"=>"...some_secret_...", "grant_type"=>"authorization_code", "code"=>"5pPCrsp0pZ84373MNaHh2cuskfc8AlbfmdwMBFIVW4n4z9aX", :redirect_uri=>"https://community.formshare.org/auth/oauth2_basic/callback"}

------------------

OAuth2 Debugging: response status 200

From POST https://formshare.ngrok.io/oauth2/token

Headers: {"content-length"=>"108", "content-type"=>"text/html; charset=UTF-8", "date"=>"Thu, 01 Sep 2022 21:42:08 GMT", "ngrok-trace-id"=>"79cdc3f1c3eae5e37a30796aebbf9bd6", "server"=>"gunicorn"}

Body: {"token_type": "Bearer", "access_token": "p0FVuwjSXL1ZINEklMAVqUlpZxSll1SgnbpE8YWP4C", "expires_in": 864000}

-----------------------------------

(oauth2_basic) Authentication failure! invalid_credentials: OAuth2::Error, {"token_type": "Bearer", "access_token": "p0FVuwjSXL1ZINEklMAVqUlpZxSll1SgnbpE8YWP4C", "expires_in": 864000}

Nous avons laissé les paramètres “oauth2 callback user id path” et “oauth2 callback user info paths” vides.

Toute idée est appréciée.

Puis-je l’utiliser pour m’authentifier auprès du service XBL de Microsoft ?

Je suppose que la logique serait similaire à ceci ?

Bonjour à tous. J’essaie de configurer ce plugin avec notre serveur Oauth2 interne avec le flux d’autorisation par code.

Lorsqu’un utilisateur clique sur « Se connecter avec Oauth », le point de terminaison /authorize fonctionne et un code est renvoyé au callback. Mais ensuite, Discourse affiche une erreur générique 500 « Oops. Le logiciel qui alimente ce forum de discussion a rencontré un problème inattendu » et le point de terminaison /token n’est pas accédé.

Le journal d’erreurs dit ceci :
OAuth2::ConnectionError (FinalDestination: all resolved IPs were disallowed) lib/final_destination/ssrf_detector.rb:74:in lookup_and_filter_ips' lib/final_destination/http.rb:13:in connect’ lib/midd

hostname discourse-app
process_id 653
application_version 702f27e6ee10ac257f5fee3f331d05f5fa5d7a45
HTTP_HOST *****
REQUEST_METHOD GET
HTTP_USER_AGENT Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36
HTTP_ACCEPT text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9
HTTP_REFERER *****
HTTP_X_FORWARDED_FOR *****
HTTP_X_REAL_IP *****
time 10:25 pm
params
code def50200babf84f7376f99fefa34369d876566b6bc0a341d8fba431999a72549ac06f6aad01df6fa43061707c525ba5d725ad
state 20139e0a134a5972566d4ddb6f7f9092a2cddb9e5216973a

D’après ce que je comprends, il y a un problème avec une adresse IP ? Actuellement, le serveur Oauth2 est hébergé dans mon environnement de développement (localhost) et les points de terminaison d’autorisation et de jeton sont configurés en conséquence. Est-ce un problème ?

Problème trouvé :

  1. Pour une raison quelconque, le point de terminaison /token n’a jamais été appelé. Après avoir rempli un maximum d’options dans les paramètres d’administration liés à l’oauth, le point de terminaison a été appelé sans réponse.
  2. J’ai oublié que c’était le serveur Discourse qui appellerait le point de terminaison /token et non le webclient. Par conséquent, le serveur ne pouvait pas atteindre mon serveur Oauth2 localhost. Placer notre serveur Oauth2 derrière un domaine a résolu le problème.

Maintenant, je peux connecter les utilisateurs existants mais je ne comprends pas comment connecter de nouveaux utilisateurs via ce plugin.
Si l’utilisateur se connecte avec oauth, il reçoit une erreur indiquant qu’il n’a pas de compte actif sur le serveur Discourse. Ce qui est normal puisqu’il s’agit d’un nouvel utilisateur.

Existe-t-il un rappel dédié pour connecter l’utilisateur au lieu de le faire entrer ? Ou un paramètre spécifique à définir pour autoriser la création de compte ?

Mon serveur oauth d’entreprise générait une réponse JSON /profile avec une petite faute de frappe dans un champ. Tout s’est bien passé après avoir corrigé la faute de frappe.
Mais je dois dire que les logs de Discourse peuvent être très trompeurs ! Rien n’allait mal avec le rappel.

Salut l’équipe,

J’ai un problème pour extraire l’ID dont j’ai besoin pour ma requête JSON utilisateur de la réponse d’autorisation. En lisant la documentation, il semble que l’ID de compte soit envoyé dans un tableau imbriqué :

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
   "access_token":"2YotnFZFEjr1zCsicMWpAA",
   "token_type":"Bearer",
   "expires_in":1800,
   "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
   "permissions":[
      {
        "accountId":123,
        "availableScopes":["contacts_view", "contacts_me", 
"contacts_edit", "finances_view", "events_view"]
      }
   ]
}

J’ai essayé de définir le chemin de l’ID utilisateur du callback oauth2 sur permissions[0].accountId, mais la valeur de mon uid est toujours vide. Malheureusement, les appels pour extraire le JSON utilisateur nécessitent cet accountId dans l’URL JSON.

J’ai réussi à faire fonctionner cela en passant permissions.first.accountId. J’ai constaté qu’en passant les permissions dans une propriété de test, le tableau était déjà analysé comme un tableau Ruby. Malheureusement, les champs semblent rejeter la syntaxe Ruby pour appeler des éléments de tableau et toute tentative d’utilisation de la syntaxe Javascript entraînerait une TypeError String to Integer. Heureusement, Ruby avait la syntaxe ci-dessus, est-ce la méthode prévue ?

J’ai réussi à faire fonctionner cela avec Authentik OAuth2, mais il y a eu quelques problèmes avec le paramètre oauth2 user json url. J’ai utilisé le point de terminaison user_info d’Authentik pour cela (/application/o/userinfo/), mais je ne savais pas comment mapper les champs. Pour ceux qui cherchent comment configurer Discourse avec l’OAuth2 d’Authentik, voici le résumé :

  • Chemin de l’ID utilisateur : preferred_username
  • Chemin du nom d’utilisateur : preferred_username
  • Chemin du nom : name
  • Chemin de l’e-mail : email
  • Chemin de l’e-mail vérifié : email_verified
  • Avatar : vide.

J’ai eu les problèmes suivants :

  1. Au début, j’ai oublié la barre oblique finale dans l’URL JSON https://DOMAIN/application/o/userinfo/. Cela a conduit la requête d’informations utilisateur (lien permanent vers la source) à retourner un code HTTP 301, ce qui a fait échouer la connexion. Je ne sais pas si la barre oblique finale devrait être là selon la spécification, mais il serait peut-être bon de gérer correctement les 301.
  2. Le débogage s’est avéré délicat. Le paramètre oauth2 debug auth a été d’une grande aide, mais… Logster tronque le journal de débogage avant de réellement afficher les données de réponse significatives. J’ai dû modifier manuellement dans le conteneur la ligne de journalisation pour
    log("user_json_response: #{user_json_response.status} #{user_json_response.headers} #{user_json_response.body}")
    
    Peut-être que cette ligne de journalisation pourrait être mise à jour ? Je suppose que cela pourrait aider d’autres personnes à trouver le chemin des attributs JSON.
4 « J'aime »

Je viens de configurer Auth0 avec le plugin et j’ai constaté que les avatars ne sont pas récupérés.

Voici les paramètres pertinents :

  DISCOURSE_OAUTH2_ENABLED: true
  DISCOURSE_OAUTH2_CLIENT_ID: '${DISCOURSE_OAUTH2_CLIENT_ID}'
  DISCOURSE_OAUTH2_CLIENT_SECRET: '${DISCOURSE_OAUTH2_CLIENT_SECRET}'
  DISCOURSE_OAUTH2_AUTHORIZE_URL: '${DISCOURSE_OAUTH2_ISSUER}/authorize?connection=xxx&login_options=yyy'
  DISCOURSE_OAUTH2_TOKEN_URL: '${DISCOURSE_OAUTH2_ISSUER}/oauth/token'
  DISCOURSE_OAUTH2_USER_JSON_URL: '${DISCOURSE_OAUTH2_ISSUER}/userinfo'
  DISCOURSE_OAUTH2_SCOPE: 'email openid profile'
  DISCOURSE_OAUTH2_JSON_USER_ID_PATH: 'sub'
  DISCOURSE_OAUTH2_JSON_USERNAME_PATH: 'nickname'
  DISCOURSE_OAUTH2_JSON_NAME_PATH: 'name'
  DISCOURSE_OAUTH2_JSON_EMAIL_PATH: 'email'
  DISCOURSE_OAUTH2_JSON_EMAIL_VERIFIED_PATH: 'email_verified'
  DISCOURSE_OAUTH2_JSON_AVATAR_PATH: 'picture'
  DISCOURSE_OAUTH2_EMAIL_VERIFIED: true
  DISCOURSE_OAUTH2_OVERRIDES_EMAIL: true
  DISCOURSE_OAUTH2_ALLOW_ASSOCIATION_CHANGE: false

Dans le journal de débogage, je peux voir que l’élément picture est défini dans la réponse JSON, mais l’avatar de l’utilisateur ne change pas, ni pour les nouveaux utilisateurs ni pour les utilisateurs existants.

Qu’ai-je manqué ?

Quelle est la meilleure façon de remplacer l’icône du bouton de connexion par une autre icône ou une image ?

.btn-social.oauth2_basic:before {
    content: url('https://www.contoso.com/path/to/image');
}

.btn-social.oauth2_basic > svg {
    display: none;
}

semble suffisant mais un peu artisanal.

2 « J'aime »

Il semble que le plugin ne mette à jour l’avatar/nom d’utilisateur que lors de la création initiale de l’utilisateur, et non à chaque connexion.

Y a-t-il un moyen de corriger cela et de faire en sorte que le plugin mette également à jour l’avatar lors de la connexion/reconnexion ?

Vous pouvez utiliser les paramètres auth overrides email, auth overrides username et auth overrides name pour que ces éléments s’appliquent lors des futures connexions. Je crains que nous n’ayons pas actuellement de paramètre similaire pour les avatars, mais ce serait pr-welcome

2 « J'aime »

Merci ! Je les ai en fait trouvés plus tard. J’ai forké le dépôt et ajouté mes propres versions pour que cela fonctionne avec Roblox, ce qui incluait la surcharge pour les avatars. Ce qui, je crois, utilise simplement la surcharge d’avatar DiscourseConnect pour que les gens ne puissent pas la changer.

Une chose que je regrette cependant, c’est que Roblox ne fournisse pas d’e-mail sur OAuth, j’ai donc malheureusement besoin qu’ils s’inscrivent avec un e-mail. mais ce n’est pas un problème pour vous haha.

Un message a été divisé en un nouveau sujet : La connexion Twitter ne fonctionne pas sur meta

Quelqu’un sait si cela fonctionne toujours ?

Oui. Je suis convaincu que ce plugin fonctionne. :+1:

1 « J'aime »

Salut, j’ai réussi à intégrer ce plugin dans mon discourse discuss.frontendlead.com, j’utilise teachable oauth https://docs.teachable.com/docs/oauth-quickstart-guide

Cependant, je veux seulement permettre aux gens de s’inscrire avec succès si et seulement s’ils ont un compte payant actuel sur Teachable. J’imagine que je dois ajouter une fonctionnalité personnalisée dans le plugin pour gérer cela ? Je me demande, est-ce que vous ou moi pouvons introduire un autre champ dans les paramètres appelé code personnalisé après oauth, qui permet aux développeurs d’effectuer des actions spécifiques après l’inscription ? Ou s’il y a de meilleures suggestions, faites-le moi savoir.

Edit : J’ai forké le dépôt et j’ai réussi à le faire fonctionner ici :

Si quelqu’un d’autre utilisant Teachable essaie de faire la même chose, mon dépôt fonctionnera directement, la seule chose est que si vous n’avez pas acheté de cours, il sera dit que vous devez aller sur mon domaine pour l’acheter. vous voudrez peut-être mettre à jour cela pour votre propre cas d’utilisation.

2 « J'aime »

C’est super !

Il y a une situation similaire avec l’enregistrement OAuth2 avec le plugin Discourse Patreon. Lorsque “Se connecter avec Patreon” est activé, il permet à toute personne ayant un compte Patreon de s’inscrire sur le site Discourse. Ce que les propriétaires de sites veulent généralement, c’est de permettre uniquement à leurs supporters de pouvoir s’inscrire sur Discourse. Je me demande si des détails sont renvoyés par Patreon qui permettraient d’ajouter une logique similaire à l’authentification Patreon ?

1 « J'aime »

J’ai exactement la même erreur que @qlands ci-dessus.

Mon plan initial était d’envoyer les informations de profil dans le token. Voyant que cela ne fonctionnait pas, j’ai réduit le code pour essayer l’approche JSON. Mais il n’arrive même pas au point d’appeler le fichier JSON.

Le message d’erreur est :

(oauth2_basic) Échec de l'authentification ! invalid_credentials: OAuth2::Error, {
  "access_token":"fa79b6fe0763862f5a8fd8",
  "token_type":"Bearer",
  "expires_in":3600,
  "scope":"profile"
}

Voyez-vous quelque chose qui ne va pas dans la réponse ci-dessus ?
Pourquoi le plugin générerait-il une erreur invalid_credentials alors que le serveur OAuth2 a répondu avec un 200 et un token ?