Protocole d'authentification concernant l'intégration d'applications

Hey all! My team and I have been looking into possible ways to integrate our [future] Discourse instance into our mobile application - Noom. Our iOS app is written in Objective-C with some Swift, and our Android application is written in Java with some Kotlin. We’ve been debating between full integration via API or web view as our goal is an SSO flow where our users can seamlessly transition from an in-app experience to our Discourse instance.

I’m currently waiting on our QA team to get back to me on which authentication protocols we currently use, but I was curious as to anyone else’s experience integrating Discourse into a mobile application with or without SSO, and what methods (if any) you found most useful throughout the process. I’m aware of Discourse’s compatibility with OAuth/2, although not aware of other potential protocols.

My apologies if this is in the wrong category!

There is a new way of authentication. However the documentation is yet to be released.

I’m going to update the documentation about delegated authentication soon, but I can give you some pointers right here.

First you need to open a browser session to discourse.site/user-api-key/new with the following parameters:

scopes: 'notifications,session_info,one_time_password',
client_id: YOUR_APP_CLIENT_ID,
nonce: GENERATED_NONCE,
auth_redirect: YOUR_APP_URL_SCHEME,
application_name: YOUR_APP_NAME,
push_url: PUSH_URL (if you are going to send PNs from Discourse to your app),
public_key: PUBLIC_KEY (generated in your app)

You can have a look at the implementation of our DiscourseMobile app for details on the above but the main idea is that your app will launch a browser screen to the URL above, asking the user to authenticate to the Discourse site and authorize your app access to it. Once user authorizes access, Discourse will redirect to YOUR_APP_URL_SCHEME?payload= with an encrypted payload. You’ll need to set up your app to decrypt the payload and store the authToken. In iOS, you should use ASWebAuthenticationSession | Apple Developer Documentation (I don’t know if there is an Android equivalent).

Your authToken can make API requests limited by the scopes requested initially, for a full list of scopes, please look under allow user api key scopes in site settings.

The one_time_password scope allows the authToken to make a request for a one-time-password. The endpoint for this is /user-api-key/otp with the parameters auth_redirect, application_name and public_key.

I will write a proper documentation shortly, but this should help you get started.

@hosna + @pmusaraj Thank you both! I will pass this information along to our QA director.

@pmusaraj Salut, existe-t-il déjà une documentation officielle accessible au public ? J’aimerais bien creuser le sujet.

Je vais m’attaquer à cette question précise au cours des prochains jours et je tiendrai à jour mes résultats, qu’ils soient positifs ou négatifs. Si quelqu’un a des guides ou des conseils, je serais ravi de les recevoir :slight_smile:

Je pense que vous cherchez User API keys specification et plus précisément la section « Flux de génération des clés API ».

Salut Penar, merci pour tes conseils. Je suis loin d’être un expert dans ce domaine ; je n’ai jamais vraiment travaillé avec de la cryptographie ou des API comme celle-ci. Je me demandais si tu pouvais m’aider à savoir comment encoder certains de ces paramètres. Je regarde ton application DiscourseMobile, mais je n’ai jamais utilisé JavaScript auparavant, donc je rencontre quelques difficultés.

nonce et client_id : S’agit-il simplement de chaînes aléatoires de 16 et 32 octets encodées en hexadécimal ?
public_key : As-tu des conseils sur la façon de le générer ? J’essaie d’utiliser cette ressource, ce qui me laisse avec un SecKeyRef. Dois-je le convertir en NSData puis l’encoder en UTF-8 ?

Aurais-tu par hasard des extraits de code en Swift ou Objective-C ? Je vais finir par comprendre, mais tout cela est nouveau pour moi, donc tout petit coup de main est apprécié.

Le nonce est une clé aléatoire, voir generateNonce ici : DiscourseMobile/js/site_manager.js at main · discourse/DiscourseMobile · GitHub

client_id est le deviceToken de l’application, il est transmis à l’application via GitHub - react-native-push-notification/ios: React Native Push Notification API for iOS. · GitHub
public_key est généré par GitHub - SamSaffron/react-native-key-pair · GitHub ; cette bibliothèque est assez petite et les parties pertinentes sont en Objective-C.

Salut Penar, merci pour les liens, très utiles.
Je progresse, mais je pense que je formate mal ma clé publique. Qu’en penses-tu ?

https://myserver.com/user-api-key/new?client_id=11223344556677889900AABBCCDDEEFF11223344556677889900AABBCCDDEEFF&nonce=11223344556677889900AABBCCDDEEFF&application_name=AppName&public_key=-----BEGIN%20PUBLIC%20KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtoNwaYpbUcX1vqxPkxRA%0D1EyfLPxkKL6zgx4Fkk9zkYbTPTWQFuqy+O1EJtVnsW+Tx8iarFLI+ypghcx22nI0%0DN/hDFYsaT/xri+LLDc790uf2UtyolgBJfkyjJDxIXXy0pdpi6f2dgKMN2holDkxf%0DTUUnZad+wE8gT8IciX1XjU97MOngSV+IDeKPLomTuTiI1Z0hJe4WDww5+53ci4o4%0DoE5A79H/Fz/QY8vDLTcBNrQ6OdJYsRqhE5M+1sNmxpqDKT+9NcAPY7yphxm1iLuV%0Dm7c6K/xrbZExGDQd1qHvggT2ldMJIsmQEnleMdfuaYLh8+EYt8LNQ8X86V0Jj+9C%0DmQIDAQAB-----END%20PUBLIC%20KEY-----&auth_redirect=appname://auth&scopes=notifications,session_info,one_time_password

Dans la console des journaux, je vois :
OpenSSL::PKey::RSAError (Ni clé PUB ni clé PRIV : erreur ASN1 imbriquée) /var/www/discourse/app/controllers/user_api_keys_controller.rb:189:in `initialize’

Merci

Je viens de publier un script pour faire exactement cela @xceph.

Vérifiez Génération de clés API utilisateur pour les tests.

Merci ! J’ai enfin fait avancer les choses. Mon problème venait de l’encodage des paramètres : j’utilisais URLQueryAllowedCharacterSet d’iOS, qui n’encode pas « / » ni « + ». Une fois que j’ai créé un script Ruby de test pour isoler la ligne qui provoquait le plantage, j’ai pu remonter la piste. Merci !

Je n’arrive pas à faire fonctionner correctement la redirection en utilisant ASWebAuthenticationSession comme l’a suggéré Penar. Les appels semblent se dérouler comme prévu : j’obtiens la fenêtre de dialogue « Autoriser » et, après avoir cliqué dessus, je suis redirigé vers un écran blanc, ce qui correspond probablement à la tentative de redirection. Cependant, celle-ci ne se termine pas et les informations ne sont pas envoyées à mon callback.

J’ai vérifié que mon URL personnalisée fonctionne correctement depuis Safari. De plus, si je supprime auth_redirect, je suis dirigé vers la page « Nous venons de générer une nouvelle clé API utilisateur à utiliser… », ce qui indique que tout fonctionne sauf la redirection.

Par ailleurs, et cela est probablement lié, j’ai remarqué que si j’essaie d’insérer un lien hypertexte dans une discussion en utilisant mon URL personnalisée, celui-ci n’est pas cliquable. Manquerais-je d’un paramètre pour autoriser les URL personnalisées ? Toute aide serait appréciée :slight_smile:

Édit : bien sûr, je trouve cela immédiatement après avoir posé la question. Dans les paramètres, vous devez effectivement définir « Autoriser les redirections d’authentification API utilisateur » sur votre schéma personnalisé.

Un écran blanc uni indique très probablement que le serveur rencontre une erreur. Lorsque vous réessayez, vérifiez les journaux (/logs) de votre site Discourse ; il y a probablement quelque chose à y trouver. Vous rencontrez probablement un problème où le même identifiant de client ne peut pas être enregistré plusieurs fois. Il faudra peut-être effacer manuellement cet identifiant de client sur l’instance Discourse, ce qui n’est pour l’instant possible que via la console Rails.

Oui, cela provenait du paramètre « Redirections d’authentification API utilisateur autorisées » dans le panneau d’administration. Il fallait le configurer et cela a fonctionné correctement.

Actuellement, je peux m’authentifier, mais je rencontre des difficultés pour décoder la charge utile sur iOS. Je peux prendre la charge utile reçue et ma clé privée, puis les décoder en Ruby ou NodeJS pour prouver que cela fonctionne, mais faire en sorte qu’iOS la décode directement s’avère plus compliqué que prévu pour moi.

Si vous examinez le code source de notre application, vous y trouverez des exemples sur la manière de procéder au décodage. Cela a nécessité un volume de code assez complexe pour que tout fonctionne.

Merci, j’ai réussi à faire fonctionner l’ensemble. Je posterai quelques extraits plus tard pour aider toute personne souhaitant réaliser cela de manière native sur iOS. Je vous remercie pour vos conseils.

Une fois le jeton API créé de cette manière, peut-il être utilisé pour authentifier une connexion dans une webview ? J’avais supposé que c’est ce que l’application faisait, mais je travaille encore sur cet aspect. Je tente de fournir à la fois un accès API et la possibilité de naviguer via le client web normal de Reface, le tout avec une seule connexion.