OAUTH2 de base - un cauchemar :-(

Salut tout le monde,

J’essaie de configurer OAUTH2 depuis 2 jours et j’ai des problèmes.

J’ai tout configuré selon la documentation, mais j’obtiens ceci tout le temps :

(oauth2_basic) Échec de l'authentification ! invalid_scope: OmniAuth::Strategies::OAuth2::CallbackError, invalid_scope | Portée(s) inconnue(s)/invalide(s)
Démarrage de GET "/auth/failure?message=invalid_scope&origin=<censuré>%2Flatest&strategy=oauth2_basic" pour 10.153.107.106 à 2024-11-27 05:30:33 +0000
Traitement par Users::OmniauthCallbacksController#failure comme HTML
  Paramètres : {"message"=>"invalid_scope", "origin"=>"<censuré>/latest", "strategy"=>"oauth2_basic"}

Ma configuration sur le tableau (concernant les scopes) est donc :

L’IdP est configuré comme ceci :

Quelqu’un a une idée pour déboguer cela un peu plus loin :

Des requêtes curl sympas, avec lesquelles je pourrais tester, ou un meilleur affichage du débogage (j’ai déjà activé l’option de débogage riche) ?

Je deviens fou avec ça :frowning:

Merci et cheers !

WS

MISE À JOUR :

Je viens de remettre les options à juste “scope”

mais ça n’a pas fonctionné :frowning:

2 « J'aime »

remarqué que notre IdP avait le mauvais scope sur son interface utilisateur…

openId est sur l’interface utilisateur
openid est le vrai :slight_smile:

une étape de plus, mais maintenant j’ai un timeout

image

après cet appel


OAuth2 Debugging: request POST https://<myAuthProvider/auth/oauth2/realms/root/realms/<realm>/access_token

Headers:
--- !ruby/hash-with-ivars:Faraday::Utils::Headers
ivars:
  :@names:
    user-agent: User-Agent
    content-type: Content-Type
elements:
  User-Agent: Faraday v2.12.1
  Content-Type: application/x-www-form-urlencoded


Body:
---
client_id: <client-id>
client_secret: <client-secret>
grant_type: authorization_code
code: <code
redirect_uri: https://<myDiscourse>/auth/oauth2_basic/callback

se terminant par

Mais comme l’appel d’autorisation pour obtenir le code a fonctionné, il ne peut y avoir de problème réseau, n’est-ce pas ?

1 « J'aime »

Lorsque j’émets l’appel avec un curl

curl --request POST \
  --url https://<IdP address>/auth/oauth2/realms/root/realms/<realm>/access_token \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data client_id=<clientId> \
  --data client_secret=<secret> \
  --data grant_type=authorization_code \
  --data code=<code > \
  --data redirect_uri=https://<discourse>/auth/oauth2_basic/callback

depuis le conteneur docker disourse, j’obtiens ceci

{
  "access_token": "<token>",
  "refresh_token": "<refresh_token>",
  "scope": "openid profile email",
  "id_token": "<censored>",
  "token_type": "Bearer",
  "expires_in": 7199
}

Cela pourrait-il être un problème à cause de ceci ?

image

Ces champs ne sont pas dans la réponse :frowning:

2 « J'aime »

personne ? :frowning:

1 « J'aime »

Je suis très intéressé par le sujet car je fais des recherches sur la quantité d’efforts qui seraient nécessaires pour ma première page de discussion pour mon quartier, puis progressivement pour les écoles de mes enfants. Je suis submergé par les notifications de toutes les différentes plateformes.
Réduire la friction vers la page est essentiel, je voudrais donc offrir mon aide. Je suis novice en Ruby, je ne peux donc offrir que quelques ressources que j’ai trouvées et des idées sur ces ressources.

Vous l’avez peut-être déjà regardé, mais vous voudrez peut-être consulter le dépôt sur github pour discourse-openid-connect

Le dépôt compte 37 contributeurs et j’imagine que l’un d’eux serait en mesure de vous aider à répondre à votre question.

J’espère que cela vous aidera car c’est une excellente question.

1 « J'aime »

Salut @thecatfix ,

Ça semble prometteur. Je vais l’essayer tout de suite et te faire un retour dès que possible.
Malheureusement, je n’ai pas le temps aujourd’hui. Peut-être que je pourrai y travailler ce week-end.

Merci beaucoup et passe un excellent week-end !

Salut,

Je n’ai pas pu attendre et je l’ai testé aujourd’hui, mais d’une manière ou d’une autre, les gars de notre département EAM ont foiré cette découverte. Ça ne marche pas et j’obtiens un client invalide - authentification échouée malgré le fait que je sois 1000% certain que les identifiants fonctionnent (testé avec des curls).
Je suis donc de retour au point de départ.

Ce qui est intéressant, c’est que j’obtiens constamment une erreur de délai d’attente (timeoutError) pour la requête vers le point de terminaison d’autorisation, et cela arrive assez rapidement.

Faraday::TimeoutError (Net::ReadTimeout with #<TCPSocket:(closed)>)

J’ai trouvé ceci, et je suis curieux de savoir si quelque chose de similaire est également possible dans Discourse ?

Je ne suis pas du tout un expert en Ruby, j’aurai donc besoin d’aide.

Je veux déboguer chaque requête gérée du côté de Discourse. Est-ce possible ?
J’ai aussi pensé à installer un proxy local pour intercepter les appels, mais avant de m’y lancer, je voulais demander s’il existe des méthodes plus simples :slight_smile:

Voulez-vous afficher le trafic HTTP brut ou pouvoir plonger dans le code ?

Le trafic brut …

des requêtes envoyées avec des en-têtes et une charge utile …

est-ce possible d’une manière ou d’une autre ?

La façon la plus simple de le faire est depuis l’intérieur du conteneur ; vous pouvez intercepter et imprimer les requêtes entre nginx et Discourse en entrant dans le conteneur et en exécutant :

apt-get update && apt-get -y install scapy
scapy

# à l'invite scapy, collez :
class Callback:
  def __init__(self):
    self.last = None
  def prn(self, p):
    if p != self.last: # les pcap sur lo capturent deux fois
      self.last = p
      p.hide_defaults()
      print(repr(p)) # cette ligne imprime le paquet, gardez-la ou laissez-la
      if scapy.packet.Raw in p.layers():
        try:
          print(p.load.decode())
        except:
          print(p.load)

sniff(filter="port 3000", iface="lo", prn=Callback().prn)
1 « J'aime »

C’est énorme, merci… je cherchais exactement un tel outil !

Hmmmm… mais
il semble que Discourse reçoit le rappel, puis…
D’après ce que je comprends, il devrait ensuite contacter l’endpoint userinfo et obtenir les informations utilisateur avec le code récupéré, mais j’obtiens une erreur HTTP 500.


GET /auth/oauth2_basic/callback?code=_B1HRB1e6kZKc8nuGLkzGC8&iss=https%3A%2F%2F%3CmyAuthDomain%3E%3A443%2Fauth%2Foauth2%2Frealms%2Froot%2Frealms%2Fintranetrealm&state=544801ae7e8262ea1667ea7531487f28e83aae232d5182b4&client_id=eadaa55a-1697-494a-8fg5-bb1137c68caa HTTP/1.0
Host: <discourseHost>
X-Request-Start: t=1732956951.485
X-Real-IP: 10.111.101.84
X-Forwarded-For: 10.131.101.84
X-Forwarded-Proto: https
Connection: close
cache-control: max-age=0
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
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.7
sec-fetch-site: same-site
sec-fetch-mode: navigate
sec-fetch-user: ?1
sec-fetch-dest: document
sec-ch-ua: "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
referer: https://discourseHost/
accept-encoding: gzip, deflate, br, zstd
accept-language: de-DE,de;q=0.9
priority: u=0, i
cookie: lbwdn=01; win=teUNUPSc8oibB......

<Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:00 type=IPv4 |<IP  ihl=5 len=52 id=7742 flags=DF frag=0 ttl=127 proto=tcp chksum=0xdf83 src=127.0.0.1 dst=127.0.0.1 |<TCP  sport=3000 dport=33574 seq=271589520 ack=3955524768 dataofs=8 flags=A window=499 chksum=0xfe28 options=[('NOP', None), ('NOP', None), ('Timestamp', (3962821542, 3962821542))] |>
<Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:00 type=IPv4 |<IP  ihl=5 len=281 id=55898 flags=DF frag=0 ttl=127 proto=tcp chksum=0x2282 src=127.0.0.1 dst=127.0.0.1 |<TCP  sport=3000 dport=33530 seq=491394 ack=1852886175 dataofs=8 flags=PA window=507 chksum=0xff0d options=[('NOP', None), ('NOP', None), ('Timestamp', (3962823893, 3962816870))] |>
<Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:00 type=IPv4 |<IP  ihl=5 len=52 id=15439 flags=DF frag=0 ttl=127 proto=tcp chksum=0xc172 src=127.0.0.1 dst=127.0.0.1 |<TCP  sport=33574 dport=3000 seq=3955524768 ack=271589749 dataofs=8 flags=A window=511 chksum=0xfe28 options=[('NOP', None), ('NOP', None), ('Timestamp', (3962831618, 3962831618))] |>
<Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:00 type=IPv4 |<IP  ihl=5 len=710 id=7744 flags=DF frag=0 ttl=127 proto=tcp chksum=0xdcef src=127.0.0.1 dst=127.0.0.1 |<TCP  sport=3000 dport=33574 seq=271589749 ack=3955524768 dataofs=8 flags=PA window=512 chksum=0xbb options=[('NOP', None), ('NOP', None), ('Timestamp', (3962831618, 3962831618))] |<Raw  load='HTTP/1.1 500 Internal Server Error\r\nDate: Sat, 30 Nov 2024 08:55:53 GMT\r\nConnection: close\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 658\r\nX-Request-Id: 7f4bafa0-7590-4b9b-8b8d-70a4fa1ae6c5\r\nX-Runtime: 10.078459\r\n\r\n' |>
HTTP/1.1 500 Internal Server Error
Date: Sat, 30 Nov 2024 08:55:53 GMT
Connection: close
Content-Type: text/html; charset=utf-8
Content-Length: 658
X-Request-Id: 7f4bafa0-7590-4b9b-8b8d-70a4fa1ae6c5
X-Runtime: 10.078459

.....

<Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:00 type=IPv4 |<IP  ihl=5 len=52 id=15439 flags=DF frag=0 ttl=127 proto=tcp chksum=0xc172 src=127.0.0.1 dst=127.0.0.1 |<TCP  sport=33574 dport=3000 seq=3955524768 ack=271589749 dataofs=8 flags=A window=511 chksum=0xfe28 options=[('NOP', None), ('NOP', None), ('Timestamp', (3962831618, 3962831618))] |>
<Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:00 type=IPv4 |<IP  ihl=5 len=710 id=7744 flags=DF frag=0 ttl=127 proto=tcp chksum=0xdcef src=127.0.0.1 dst=127.0.0.1 |<TCP  sport=3000 dport=33574 seq=271589749 ack=3955524768 dataofs=8 flags=PA window=512 chksum=0xbb options=[('NOP', None), ('NOP', None), ('Timestamp', (3962831618, 3962831618))] |<Raw  load='<!DOCTYPE html>\n<html>\n<head>\n  <title>Oops - Error 500</title>\n  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">\n</head>\n<body>\n    <h1>Oops</h1>\n    <p>The software powering this discussion forum encountered an unexpected problem. We apologize for the inconvenience.</p>\n    <p>Detailed information about the error was logged, and an automatic notification generated. We\'ll take a look at it.</p>\n    <p>No further action is necessary. However, if the error condition persists, you can provide additional detail, including steps to reproduce the error, by posting a discussion topic in the site\'s feedback category.</p>\n</body>\n</html>\n' |>
<!DOCTYPE html>
<html>
<head>
  <title>Oops - Error 500</title>
.....

Dans les logs, cela imprime ces lignes :

Une capture d'écran montrant une erreur réseau avec un délai d'attente, incluant les en-têtes HTTP et les détails de l'erreur. (Légendé par l'IA)

Il me semble que Discourse a des problèmes pour gérer le rappel, et j’aurais maintenant besoin d’un aperçu plus approfondi de ce qui se passe en coulisses dans Discourse.

Je n’obtiens aucun indice dans les journaux malgré l’activation de tous les modes de débogage que je connais.

Hmmm, je suis vraiment bloqué avec ça et c’est frustrant :frowning:

Salut tout le monde,

Je suis beaucoup plus loin maintenant :slight_smile:

Cela avait à voir avec notre infrastructure EAM…

Maintenant, j’ai le problème suivant, cependant :smiley:

Mon userinfotoken ressemble à ceci :


{
  "company-i": "A1",
  "accounting-code": "5806",
  "given_name": "Mister",
  "family_name": "Bean",
  "name": "Mister Bean",
  "departmentnumber": "Covert Operations",
  "salutation": "Dude",
  "description": "Some description",
  "preferredlanguage": "DE",
  "inumber": "723jfio-7zwe8489",
  "employeenumber": "36484332",
  "employeetype": "BigCompany",
  "uid": "f57383",
  "adupn": "f57383@europe.bigcom.corp",
  "uniqueuid": "f57383",
  "uniqueuidq": "f57383",
  "loginname": "f57383",
  "email": "Mister.Bean@bigcom.corp",
  "sub": "f57383",
  "subname": "f57383"
}

Et je reçois cette erreur dans les logs :


ActiveRecord::NotNullViolation (PG::NotNullViolation: ERROR:  null value in column "provider_uid" of relation "user_associated_accounts" violates not-null constraint
DETAIL:  Failing row contains (6, oauth2_basic, null, null, 2024-12-03 13:06:49.831182, {"name": "Mister Bean", "email": "Mister.Bean@bigcom.corp", "user..., {"token": "dszghsdhsdfoph", "expires": true, "expir..., {}, 2024-12-03 13:06:49.831362, 2024-12-03 13:06:49.831362).

Discourse ne semble pas être capable de mapper la réponse de l’endpoint userinfo à ses noms internes.

J’ai essayé de configurer cela via l’interface utilisateur :

mais probablement mal.

Quelqu’un pourrait-il me donner un indice sur la façon de configurer cela ?

Merci et cheers,

WS

Le champ oauth2 json user id path est-il rempli ?

Assurez-vous que ces valeurs sont définies : Configure sign up and log in with Auth0 using the OAuth2 Basic Plugin.

Salut,

Merci pour votre réponse.

Il semble que mon fournisseur n’envoie pas les champs provider_name et provider_uid, et par conséquent, Discourse essaie de créer l’entrée sans ces champs et obtient une violation de NotNull.
Mais, n’est-ce pas la responsabilité du plugin (oauth2_basic) de remplir ces valeurs (s’il n’y en a pas qui proviennent du fournisseur d’identité distant) ?
Je m’attendrais à quelque chose comme :

provider_name: “oauth2_basic”
provider_uid: “1234”

Utilisé automatiquement à chaque connexion utilisateur… :frowning:

Le provider_name est défini sur oauth2_basic et le provider_uid sera ce que vous remplirez dans oauth2 json user id path.

Il vous suffit de définir ce paramètre de site sur le chemin de l’identifiant en fonction de ce que votre fournisseur renvoie. Le plugin utilise ce chemin JSON pour remplir le provider_uid, donc s’il est actuellement vide ou s’il s’agit d’un chemin qui n’existe pas, la valeur sera nil.

D’après votre JSON ci-dessus, il semble que la valeur doive être sub.

2 « J'aime »

Mais j’ai fait ça…

Vous avez raison, le chemin devrait être « sub », mais je l’ai configuré ici

Attendez… Vous aviez raison, et je suis stupide :frowning:

Mauvais réglage…

Ça marche maintenant !

2 « J'aime »

Y a-t-il un moyen de sauvegarder ces paramètres quelque part :smiley:
faire une sauvegarde des paramètres de configuration uniquement ?

Sont-ils dans la base de données ?

C’est super.

Tous les paramètres du site sont enregistrés dans la base de données. En supposant que vous ayez accès à la console Rails, ce qui suit devrait vous montrer une liste des paramètres.

SiteSetting.where("name LIKE 'oauth2_%'").pluck(:name, :value)

Vous pouvez également simplement créer une sauvegarde.