Use discourse for SSO in a non-web app?


(Chris Beck) #1

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: Registration Process or linking to other DB
"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?


(Chris Beck) #2

Looking in another thread I saw this post:

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?


(Dean Taylor) #3

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.


(Kane York) #4

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…


(Dean Taylor) #5

However the responses will only ever authenticate them against localhost.


(Kane York) #6

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.


(Dean Taylor) #7

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.


(Chris Beck) #8

@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.


(Dean Taylor) #9

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.


Log in with Discourse?
(Chris Beck) #10

Thanks, this was quite helpful!


(Chris Beck) #11

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.