Puis-je utiliser l'API Discourse pour authentifier les utilisateurs dans une autre application ?

J’ai un forum Discourse fonctionnel, où les utilisateurs peuvent créer une connexion locale (nom d’utilisateur + mot de passe).

J’aimerais réutiliser leur nom d’utilisateur et leur mot de passe dans une autre application. En d’autres termes : les utilisateurs entreraient leur nom d’utilisateur et leur mot de passe dans l’autre application, et l’application devrait être capable de vérifier s’il s’agit d’une connexion valide pour le forum.

J’ai lu la documentation de l’API Discourse. Beaucoup de choses sont possibles, y compris la définition du nom d’utilisateur et du mot de passe pour un utilisateur donné, mais je n’ai pas trouvé de point d’accès API pour valider un nom d’utilisateur et un mot de passe existants par rapport à la liste des utilisateurs du forum.

Je suppose qu’un tel point d’accès API doit exister, car le forum doit être capable de le faire pour connecter un utilisateur via l’interface web.

Quel est le point d’accès API pour vérifier un nom d’utilisateur et un mot de passe afin de se connecter au forum ?

3 « J'aime »

Plus directement, vous pouvez utiliser DiscourseConnect comme mécanisme pour valider les utilisateurs ou, en amont de votre application, utiliser discourse-auth-proxy.

Ce sont des méthodes suggérées pour authentifier les utilisateurs au lieu de gérer directement les identifiants de connexion. Cela signifie également que vous n’avez pas besoin d’essayer de gérer les détails de la 2FA.

5 « J'aime »

En fait, mon « autre application » est une application de bureau, pas une application web. Je ne pense pas que discourse-auth-proxy fonctionnera dans ce cas.

Sur la page DiscourseConnect, l’une des premières déclarations est la suivante :

De nombreux sites souhaitant s’intégrer à un site Discourse veulent garder toute l’inscription des utilisateurs sur un site séparé. Dans une telle configuration, toutes les opérations de connexion doivent être externalisées vers ce site différent.

C’est exactement le contraire de ce que je veux faire : je veux externaliser toutes les opérations de connexion vers Discourse. Existe-t-il un moyen d’utiliser DiscourseConnect pour faire cela ?

1 « J'aime »

Oui, absolument.

La chose délicate est qu’il existe un secret partagé entre le fournisseur (Discourse) et le consommateur (votre application). Si vous distribuez votre application, les utilisateurs auront accès à tous les secrets qu’elle contient.

Placer un proxy d’authentification devant un service Web minimal personnalisé qui donne un jeton signé à votre application pourrait bien fonctionner.

Je suis sûr qu’il existe d’autres moyens de faire cela auxquels je ne pense pas.

Faites-vous référence à la clé API ? Il semble possible de créer une clé API « granulaire », qui n’a accès qu’à des points de terminaison API spécifiques. Il ne m’est toujours pas clair quels points de terminaison seraient requis si j’utilisais cette approche. Le savez-vous ?

Oui, un service Web minimal avec un proxy d’authentification pourrait être une bonne solution ; je devrai expérimenter un peu pour le découvrir.

Pas exactement - il s’agirait de la valeur discourse connect provider secrets pour l’application qui devrait être définie en conjonction avec enable discourse connect provider.

Plus d’informations à ce sujet sont expliquées ici : Use Discourse as an identity provider (SSO, DiscourseConnect)

1 « J'aime »

Pour une application de bureau, peut-être que cette méthode utilisée dans cet exemple React Native peut vous être utile :

https://github.com/pmusaraj/discourse-mobile-single-site-app/blob/main/js/Authenticate.js

1 « J'aime »

Si je comprends bien, cette méthode signifierait que l’utilisateur se connecte via un navigateur. Cela peut fonctionner, bien que j’espérais trouver une méthode où le nom d’utilisateur et le mot de passe peuvent être saisis dans notre application de bureau, sans ouvrir de navigateur.

Je comprends que l’approche que j’ai en tête ne prendra pas en charge l’authentification à deux facteurs (TFA) à moins que je ne l’implémente moi-même, et qu’elle ne prendra pas en charge les connexions via des fournisseurs tiers (Google, Facebook, Discord, …).

Je ne pense pas que ce soit explicitement pris en charge, mais je créerais une session de la même manière que l’utilisateur le ferait dans un navigateur Web :


curl 'https://try.discourse.org/session' \
  -H 'sec-ch-ua: "Chromium";v="113", "Not-A.Brand";v="24"' \
  -H 'Discourse-Present: true' \
  -H 'DNT: 1' \
  -H 'X-CSRF-Token: …' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'User-Agent: …' \
  -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \
  -H 'Accept: */*' \
  -H 'Referer: https://try.discourse.org/' \
  -H 'X-Requested-With: XMLHttpRequest' \
  -H 'sec-ch-ua-platform: "Linux"' \
  --data-raw 'login=demouser&password=demopassword&second_factor_method=1&timezone=America%2FToronto' \
  --compressed

et dupliquer cette logique de connexion dans l’application.

D’après ce que je comprends pour le moment, il semble que la méthode utilisée dans l’exemple Reactive Native puisse être transposée à notre application de bureau (qui est en Python).

Le point d’accès API utilisé semble être <site>/session, et il prend un nom d’utilisateur, un mot de passe et un jeton csrf. Le jeton csrf peut être obtenu à partir de <site>/session/csrf.

C’est très proche de ce que je cherchais. Je pense que je vais essayer ça, je vous dirai si ça fonctionne pour moi.

Le point d’accès API <site>/session est-il documenté quelque part ?

1 « J'aime »

La meilleure façon d’obtenir ce que vous voulez dans une application de bureau est d’utiliser les clés d’API utilisateur.

Vous avez besoin d’une interface web, soit dans l’application, soit en ouvrant le navigateur, mais si vous faites de votre application un gestionnaire pour le protocole utilisé par les applications mobiles, vous pouvez facilement obtenir le jeton de cette façon et n’avoir à utiliser le navigateur qu’à nouveau si le jeton expire ou s’ils utilisent un appareil différent.

Mon expérience personnelle avec cela est que l’utilisation des clés d’API utilisateur est une option beaucoup plus sûre et plus simple que d’essayer d’utiliser les points de terminaison de session. :slight_smile:

2 « J'aime »

Voici 20 lignes de code Python qui font approximativement la même chose que le code React Native référencé par @renato (sauf qu’il n’y a pas de compatibilité avec Discourse 2.5 - je n’en ai pas besoin)

Cela fonctionne bien, en supposant que vous utilisez une connexion de base par nom d’utilisateur et mot de passe. Je vais toujours examiner les méthodes alternatives, en utilisant la connexion SSO de Discourse telle que configurée dans l’instance Discourse.

import requests
import json

def discourse_authenticate(url, name, password):
    session = requests.Session()
    session.headers.update({'X-Requested-With': 'XMLHttpRequest'})
    r1 = session.get(url + '/session/csrf')
    csrf_token = json.loads(r1.text).get('csrf')
    r2 = session.post(url + '/session',
        data={
            'login': name,
            'password': password,
            'authenticity_token': csrf_token,
        },
    )
    if r2.status_code != 200:
        return None
    return json.loads(r2.text)

J’ai essayé d’appliquer cela mais je n’arrive pas à le faire fonctionner. Ci-dessous se trouve un code Python (simplifié) qui génère une URL pour .../session/sso_provider. Lorsque j’essaie, j’obtiens Login Error. Aucune idée de ce que cela signifie.

import secrets
import base64
import urllib.parse
import hmac
import hashlib

forum_url = 'https://forum.embeetle.com'
target_url = 'https://embeetle.com/#account'
sso_secret = b'JCLSVcqbAnEPXz2p2xBY'

nonce = secrets.token_urlsafe()
payload = f'nonce={nonce}&return_sso_url={target_url}'
payload_base64 = base64.b64encode(payload.encode('utf-8')).decode()
payload_for_url = urllib.parse.quote(payload_base64)

payload_for_url = 'bm9uY2U9YklKeEU1WWw2OFhjSkJydGlwSU15UTRZeVlMeWd6ZzQyUU9mOFo0SWF5QSZyZXR1cm5fc3NvX3VybD1odHRwczovL2VtYmVldGxlLmNvbS8jYWNjb3VudA%3D%3D'

signature = hmac.new(
    sso_secret, payload_for_url.encode('utf-8'), hashlib.sha256
).hexdigest()

print(f'{forum_url}/session/sso_provider?sso={payload_for_url}&sig={signature}')

Par exemple, une exécution pourrait générer l’URL dans la commande curl ci-dessous :

johan@morla:~/sa> curl 'https://forum.embeetle.com/session/sso_provider?sso=bm9uY2U9YklKeEU1WWw2OFhjSkJydGlwSU15UTRZeVlMeWd6ZzQyUU9mOFo0SWF5QSZyZXR1cm5fc3NvX3VybD1odHRwczovL2VtYmVldGxlLmNvbS8jYWNjb3VudA%3D%3D&sig=a392ebb81b93ba7411290fbd00240921ae053bbb82998830dda994c8a71853da'
Login Errorjohan@morla:~/sa> 

En tant qu’administrateur, activez verbose discourse connect logging, essayez, puis vérifiez /logs sur votre forum pour voir des erreurs plus détaillées, par exemple https://forum.embeetle.com/logs

Vous verrez par exemple :

Vous devez signer la charge utile, pas la charge utile citée, par exemple :

signature = hmac.new(
  sso_secret, payload_base64.encode('utf-8'), hashlib.sha256
).hexdigest()

Et ensuite, cela fonctionne !

Au fait, vous devez révoquer et changer ce secret immédiatement car toute personne l’ayant peut se connecter à votre application comme je viens de le faire en testant ceci.

3 « J'aime »

Ne vous inquiétez pas, la page vers laquelle je redirige est une page publique, juste pour les tests.

J’ai quand même changé le secret.

2 « J'aime »

Quelles sont vos 3 meilleures suggestions open-source pour cette tâche spécifique ?

J’utilise Nginx, mais cela pourrait probablement être géré avec un développement plus robuste comme Keycloak ?

Par auth-proxy, j’entends ceci : GitHub - discourse/discourse-auth-proxy: An http proxy that uses the DiscourseConnect protocol to authenticate users

2 « J'aime »

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.