Protocolo de autenticación re: integración de aplicaciones

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!

1 me gusta

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

3 Me gusta

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.

5 Me gusta

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

1 me gusta

@pmusaraj, hola, ¿ya hay alguna documentación oficial disponible para el público? Me encantaría profundizar más.

Voy a abordar exactamente esto en los próximos días; actualizaré sobre mi éxito o fracaso. Si alguien tiene alguna guía o consejo, lo agradecería mucho :slight_smile:

Creo que estás buscando User API keys specification y específicamente la sección “Flujo de generación de claves de API”.

1 me gusta

¡Hola, Penar! Gracias por la orientación. Estoy lejos de ser un experto en esto; no he trabajado mucho con criptografía ni con APIs como esta antes. Me preguntaba si podrías ayudarme a saber cómo codificar algunos de estos parámetros. Estoy revisando tu aplicación DiscourseMobile, pero nunca he usado JavaScript antes, así que tengo algunas dificultades.

nonce y client_id: ¿Son simplemente cadenas aleatorias de 16 y 32 bytes codificadas en hexadecimal?
public_key: ¿Tienes alguna orientación sobre cómo generar esto? Estoy intentando usar el recurso aquí, lo que me dejaría con un SecKeyRef. ¿Debo convertirlo a NSData y luego codificarlo en UTF-8?

¿Acaso tendrías algún fragmento de código en Swift u Objective-C? Lo resolveré, pero como todo esto es nuevo para mí, cualquier ayuda será muy apreciada.

nonce es una clave aleatoria, consulta generateNonce aquí: DiscourseMobile/js/site_manager.js at main · discourse/DiscourseMobile · GitHub

client_id es el deviceToken de la aplicación; se pasa a la aplicación mediante GitHub - react-native-push-notification/ios: React Native Push Notification API for iOS. · GitHub
public_key se genera con GitHub - SamSaffron/react-native-key-pair · GitHub; esa biblioteca es bastante pequeña y las partes relevantes están en Objective C.

4 Me gusta

Hola Penar, gracias por los enlaces, son muy útiles.
Estoy avanzando, pero creo que estoy dando un formato incorrecto a mi clave pública. ¿Cómo se ve el formato en esto?

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

En la consola de registros veo:
OpenSSL::PKey::RSAError (Ni clave PUB ni clave PRIV: error ASN1 anidado) /var/www/discourse/app/controllers/user_api_keys_controller.rb:189:in `initialize’

Gracias.

Acabo de publicar un script para hacer exactamente esto, @xceph.

Consulta Generación de claves de API de usuario para pruebas.

3 Me gusta

¡Gracias! Justo logré avances; mi problema estaba en la codificación de los parámetros. Estaba usando URLQueryAllowedCharacterSet de iOS, que no codifica “/” ni “+”. Una vez que creé un script de prueba en Ruby para aislar la línea donde se producía el fallo, pude trabajar hacia atrás. ¡Gracias!

2 Me gusta

Parece que no puedo hacer que la redirección funcione correctamente, usando ASWebAuthenticationSession como sugirió Penar. Las llamadas parecen funcionar como se espera: obtengo el cuadro de diálogo “Autorizar” y, al hacer clic en él, me llevan a una pantalla blanca en blanco, lo que supongo que es el intento de realizar la redirección, pero no se cierra ni envía la información a mi callback.

He probado que mi URL personalizada funciona correctamente desde Safari, y además, si elimino auth_redirect, se me muestra el sitio “Acabamos de generar una nueva clave de API de usuario para usar…”, por lo que todo parece funcionar excepto la redirección.

Además, y esto probablemente esté relacionado, he notado que si intento poner un hipervínculo en una discusión usando mi URL personalizada, no es clicable. ¿Estoy olvidando alguna configuración para permitir URLs personalizadas? Agradezco cualquier ayuda :slight_smile:

edición: por supuesto, lo encuentro inmediatamente después de preguntar. En la configuración, efectivamente necesitas establecer “Redirecciones de autenticación de API de usuario permitidas” en tu esquema personalizado.

Una pantalla en blanco suele ser señal de que el servidor está encontrando un error. Cuando lo intentes de nuevo, revisa los /logs de tu sitio de Discourse; probablemente haya algo allí. Es probable que estés teniendo un problema porque el mismo ID de cliente no puede registrarse varias veces. Es posible que necesites borrar manualmente ese ID de cliente en la instancia de Discourse; por ahora, esto solo es posible a través de la consola de Rails.

2 Me gusta

Sí, fue por la configuración: “Redirecciones de autenticación de API de usuario permitidas” en el panel de administración; fue necesario configurarla y funcionó correctamente.

Actualmente puedo autenticarme, pero tengo problemas para decodificar la carga útil en iOS. Puedo tomar la carga útil recibida y mi clave privada y decodificarlos en Ruby o NodeJS para demostrar que funciona, pero lograr que iOS la decodifique directamente es más complicado de lo que pensaba.

1 me gusta

Si revisas el código fuente de nuestra aplicación, encontrarás ejemplos sobre cómo realizar la decodificación. Fue bastante complicado escribir todo el código necesario para que funcionara correctamente.

4 Me gusta

Gracias, logré que todo funcione. Publicaré algunos fragmentos de código más adelante para ayudar a cualquier otra persona que quiera hacer esto de forma nativa en iOS. Agradezco la orientación.

Una vez que hayas creado el token de API de esta manera, ¿puede usarse para autenticar un inicio de sesión en un WebView? Asumí que eso era lo que hacía la aplicación, pero aún estoy trabajando en esa parte. Estoy tratando de proporcionar tanto el acceso a la API como la capacidad de navegar a través del cliente web normal de Reface con un único inicio de sesión.

1 me gusta