Use discourse for SSO in a non-web app?

I am building a desktop application (not a web app) which includes a networking component, and I would like to be able to have a discourse forum associated to it and use the same authentication credentials for the application and the forum. Mainly, I am hoping to leverage discourse for the forum and for auth, since it already has a nice forum and robust, customizable auth and supports “reset password via email”.

The difficulty is that when users are running the application, they will enter their credentials there, not in a website.

In the instructions that I’ve found for using discourse as an SSO provider, they suggest that I generate a nonce, hash it appropriately, direct the user to a discourse url, and provide an expected return url.

What I really want though, is something simpler, more like “I give you an http query with a username and password, or, username + nonce + hash of password with nonce, you give me up or down”.

I realize that this is somewhat counter to the idea of allowing multiple auth, e.g. google, facebook logins, which is a major feature which discourse provides. However as I understand discourse also provides basic username / password auth – that’s the only part that I would use.

What concerns me more is that it doesn’t appear that I can get discourse to give me an http or other interface like that. Is this correct?

An alternative would be to try to dig the password info out of the database directly.

However this was strongly discouraged here: https://meta.discourse.org/t/registration-process-or-linking-to-other-db/29537
“Using two auth points and two different schemes would just be weird and error prone”

The thing is that, if I am forced to implement SSO in my app and not use discourse, then I have to implement email for password reset which I really don’t want to do from the ground up. I could try to use CAS or something in addition to discourse and make discourse use that for SSO… but I would really like to avoid adding another complex technology like this just for auth.

What I am considering to do is try to hack the discourse “basic auth” module so that it supports (local) connections from an intermediate server process, which I will create, which will talk to the desktop app, and on that channel, direct “user/password” queries would be supported. (Or perhaps, no hack is necessary and I can just use the discourse ruby api?) The reason would be that then there is only one db, no multiple auth points, discourse handles the password-reset emails for me.

Does this make sense?
Does it already exist?
Am I missing something obvious?
Should I be looking at something other than discourse if I am considering doing this?

Looking in another thread I saw this post:

https://meta.discourse.org/t/registration-process-or-linking-to-other-db/29537/16

This suggests that I can simply HTTP POST a username/password combo to discourse and it will respond appropriately – is there any documentation on this anywhere, or examples?

Here is one way, there are probably security implications that are deeper than this to think about - but this should give you the gist.

unless i’ve forgotten something important

A few apps that work like this run a “mini web server” inside themselves on a non-fixed port (i.e. it finds the first free port).

Effectively turning app into a web app.

See the this thread regarding using Discourse as an SSO provider:

You’ll need to think carefully about avoid leaking sso_secret - hence the introduction of YOUR_SERVER in the process flow below. But you don’t just want to recreate the problem further along the chain.

The process follow looks something like this:

  1. Start internal HTTP server (LOCAL_SERVER)
  • finding first free port > 1024 so you don’t need admin rights.
  1. Displaying the in-app browser window
  • this browser windows will effectively just loads https://YOUR_SERVER/some-path/?somesecurityA=somevalueA
  1. Generate the redirection URL (YOUR_SERVER)
  • Have your own web site (a simple PHP site will do) - this is not inside the app
  • This is responsible for keeping your sso_secret secret and playing it’s part in generating the return_sso_url
  • You’ll need to manage the security here carefully to pass the minimal amount of details (port number) each way between the app and YOUR_SERVER and securing your server from abuse i.e. somesecurityA=somevalueA.
  • perhaps always assume localhost is a part of the security measure.
  • generate the appropriate Discourse return_sso_url URL - this will some URL like YOUR_SERVER/check-discourse-response?somesecurityB=somevalueB
  • redirect to DISCOURSE_ROOT_URL/session/sso_provider?sso=URL_ENCODED_PAYLOAD&sig=HEX_SIGNATURE
  1. User signs into Discourse in the in-app browser window
  • On success the user will be redirected to YOUR_SERVER/check-discourse-response?somesecurityB=somevalueB
  1. Check the Discourse SSO response (YOUR_SERVER)
  • Check the security values etc i.e. somesecurityB=somevalueB
  • On success redirect to localhost:{port_number}/another-path/?some-other-security=some-other-value
  1. Handle HTTP request response to the local in-app web server (LOCAL_SERVER)
  • actually check any security measures you have.
  • do something nice like close the auth dialog automatically.

Doing this you’ll get support for whatever SSO providers your Discourse instance has setup i.e. Google, Twitter, Facebook, GitHub etc.

2 Likes

In other words: no, this process is not possible for an Android or iPhone app, because now they can forge the responses by disassembling the app…

2 Likes

However the responses will only ever authenticate them against localhost.

Wait, what? Then that scheme is utterly useless unless you’re feature-gating based on the username in a way that editing the app will get around.

Also keep in mind that Discourse can only have one sso_secret at a time.

2 Likes

The initial post didn’t mention anything other than authenticating the user has an account on the Discourse instance.

Any feature-gating can be worked around by “editing the app”.

There is no need for more than one with the YOUR_SERVER in-between.

@DeanMarkTaylor:

I should have given more details I suppose: My app doesn’t have an “in-app browser window” and I don’t really want to add something like this to the app. The app is a game – potentially it’s running fullscreen on a desktop or on a phone, and I don’t really want to minimize it or cause a screen resolution change to bring up the users’ native web browser, just so they can authenticate. Basically I’m trying to figure out how to replace the web interface for authentication.

Pretty sure every target platform has a “WebView” of some kind to do this, no problems on mobile.

Certainly with a Full-Screen Windows app it would be possible to use a WebView without changing resolution or needing to minimize.
Although the popup authentication windows that Facebook, Google, etc create might look a bit odd depending on resolution.

The devil is in the details.


You could always simply mimic the requests that a browser would make.

You can complete this yourself on the command line using CURL.

The following assumes:

  • Your Discourse instance is at https://try.example.com
  • Username is ExampleUser1
  • Password is ExamplePasswordXX!

1. Initially make a request to get both _forum_session cookie and CSRF-Token

curl -v "https://try.example.com/session/csrf" -H "X-CSRF-Token: undefined" -H "Referer: https://try.example.com/" -H "X-Requested-With: XMLHttpRequest"

2. Note the _forum_session cookie value from header and csrf JSON result in body

< Set-Cookie: _forum_session=XXXXXXXXXYYYYYYYYZZZZZZ; path=/; HttpOnly; Secure

{"csrf":"XXXXXXXXaaaaaaaaaaaaXXXXXXXXXXXXXX=="}

3. Attempt a login request

curl -v "https://try.example.com/session" -H "Origin: https://try.example.com" -H "X-CSRF-Token: XXXXXXXXaaaaaaaaaaaaXXXXXXXXXXXXXX==" -H "Cookie: _forum_session=XXXXXXXXXYYYYYYYYZZZZZZ" -H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" -H "Referer: https://try.example.com/" -H "X-Requested-With: XMLHttpRequest" --data "login=ExampleUser1&password=ExamplePasswordXX!"

4. Read the response

Expect a JSON response back containing the users basic profile info.

7 Likes

Thanks, this was quite helpful!

1 Like

So, as a followup, which may be helpful to who searches for this question:

What I have ultimately decided to do is, just implement SSO separately in my app as originally suggested. Sending password recovery emails and such is actually not too hard, it doesn’t require you to operate your own SMTP server which is what I originally feared. Indeed discourse doesn’t have an internal SMTP server either.

By implementing SSO on my own, I can make an interface which is customized for my app and which I know is stable and easy to use, (rather than relying on implementation details of discourse’s basic SSO which I assume are subject to change anyways), and then make a separate web interface matching the requirements of discourse.

There are definitely pitfalls associated to trying to roll your own auth / security, esp. for a small team with not a lot of expertise in this, but if you rely on SSL for crypto, as you should, then the majority of the problem is already solved, it seems.

Especially, there’s no particular need to use some large enterprise solution for this in a small project, afaict.

Hi,
I am trying to develop mobile app for discourse using flutter.
I sent GET request of /session/csrf.json to get a csrf token in my mobile app. Next I added header x-csrf-token to send a POST request to upload avatar.

Server log is:

Started POST "/uploads.json" for 98.199.193.94 at 2020-05-16 04:34:48 +0000
Processing by UploadsController#create as JSON
  Parameters: {"type"=>"avatar", "user_id"=>"639", "files"=>[#<ActionDispatch::Http::UploadedFile:0x00007f0b57b32478 @tempfile=#<Tempfile:/tmp/RackMultipart20200516-783-12pa37a.jpg>, @original_filename="image_picker_CE009BE9-D964-459D-B59C-ABE78F1CC5DF-20639-0000AE2EE903C2FC.jpg", @content_type="application/octet-stream", @headers="content-disposition: form-data; name=\"files[]\"; filename=\"image_picker_CE009BE9-D964-459D-B59C-ABE78F1CC5DF-20639-0000AE2EE903C2FC.jpg\"\r\ncontent-type: application/octet-stream\r\n">]}
Can't verify CSRF token authenticity.

So what is the right way to get valid csrf token for mobile app?

Hi so I’m trying to do a similar thing to the OP i.e. integrate this into an interactive app, a C++ windows app. Lets call it a game for arguments sake.

So I can use CURL to issue commands to my discourse instance (as shown above) and that works but what are the security implications of this?

For example the password is being passed as plain text above is there anyway to send an encrypted version? Is this how a web browser would send the password? I’m wanting to store the encrypted password in my app.

Also what would be the CURL commands for creating a new user?

Does anybody know of a C++ project that has public source I can follow - I had a look on GitHub with no success.

Many thanks