21

I have an existing rails app that is using devise as it's user authentication. I added a discourse forum and everything went smoothly and it resides on a subdomain. I have read the post at https://meta.discourse.org/t/official-single-sign-on-for-discourse/13045 but still don't know what to do with the devise side of things once the user logs in on the existing rails site. Currently this is the process as I understand it:

Step1: User hits Discourse forum on subdomain. User needs to login so clicks login button.

Step2: User is sent to the login page on the existing rails site.

Step3: User logs in on rails site.

Step4: User should be redirected to discourse forum subdomain logged in.

My question is - What do I need to to do to make it so that when a user logs in on step 3 they get redirected back to the subdomain? Has anyone successfully implemented this? I saw this code snippet on that walkthrough page:

  class DiscourseSsoController < ApplicationController
  def sso
    secret = "MY_SECRET_STRING"
    sso = SingleSignOn.parse(request.query_string, secret)
    sso.email = "user@email.com"
    sso.name = "Bill Hicks"
    sso.username = "bill@hicks.com"
    sso.external_id = "123" # unique to your application
    sso.sso_secret = secret

    redirect_to sso.to_url("http://l.discourse/session/sso_login")
  end
end

Is this what I would need to add in my existing rails app? I'm guessing the parse checks if that information is in the url and if so it redirects once it finishes the devise login process, and if not it just functions as usual. Would I place this code somewhere in the devise files?

cjiro
  • 325
  • 2
  • 7

2 Answers2

41

This is pretty straightforward. Following on from the instructions at https://meta.discourse.org/t/official-single-sign-on-for-discourse/13045 and extrapolating a little, I have this working:

1) Put the reference implementation - https://github.com/discourse/discourse/blob/master/lib/single_sign_on.rb - in your #{Rails.root}/lib directory

2) Add this route to routes.rb

get 'discourse/sso' => 'discourse_sso#sso'

3) Put this controller in your app/controllers directory

require 'single_sign_on'

class DiscourseSsoController < ApplicationController
  before_action :authenticate_user! # ensures user must login

  def sso
    secret = "MY_SECRET_STRING"
    sso = SingleSignOn.parse(request.query_string, secret)
    sso.email = current_user.email # from devise
    sso.name = current_user.full_name # this is a custom method on the User class
    sso.username = current_user.email # from devise
    sso.external_id = current_user.id # from devise
    sso.sso_secret = secret

    redirect_to sso.to_url("http://your_discource_server/session/sso_login")
  end
end

4) Set up the SSO config in discourse to have the following

sso url: http://your_rails_server/discourse/sso
sso secret : what you set as MY_SECRET_STRING above

5) Disable other login types in discourse.

6) Try to login in discourse. It should work...

DanSingerman
  • 36,066
  • 13
  • 81
  • 92
  • I think it should be `before_filter` instead of `before_action` in the controller. – Kenny Meyer Jan 30 '15 at 16:59
  • In Rails 4 it is idiomatic to use before_action http://stackoverflow.com/questions/16519828/rails-4-before-filter-vs-before-action – DanSingerman Feb 01 '15 at 22:57
  • request.query_string is going nil, where it is coming from? Can you please help me to know why is it so? I have followed your reply. – kamal Feb 05 '16 at 12:00
  • @DanSingerman what is the first route we should hit on our existing rails app, I am currently hitting, http://localhost:3000/discourse/sso but getting error in generating payload – kamal Feb 09 '16 at 08:38
  • @kamal you should be hitting that after attempting to sign to discourse (with a bunch of encrypted URL params too) – DanSingerman Feb 09 '16 at 11:33
  • Hi all, can you all help to show a bit more details how to make this work in the local development? How to configure sudomain disourse forum with top domain in development? Please help all. Thanks. – Sinal Apr 30 '16 at 04:53
  • 1
    with the newer version of Rails, you also need to pass `allow_other_host: true` to the redirect method – Octopussy Jan 31 '22 at 19:05
0

thank @DanSingerman

They updated since you posted the answer.

  1. The controller now
    class DiscourseSsoController < ApplicationController
      def sso
       secret = "MY_SECRET_STRING"
       sso = DiscourseApi::SingleSignOn.parse(request.query_string, secret)
       sso.email = "user@email.com"
       sso.name = "Bill Hicks"
       sso.username = "bill@hicks.com"
       sso.external_id = "123" # unique id for each user of your application
       sso.sso_secret = secret

       redirect_to sso.to_url("http://l.discourse/session/sso_login")
      end
    end
  1. Install the Discourse API gem or copy the SingleSignOn class in your lib folder. This class is in the gem repo: https://github.com/discourse/discourse_api/blob/main/lib/discourse_api/single_sign_on.rb

  2. About the query_string: if you are testing locally you should simulate the request coming from Discourse as it will contain a query with 2 parameters, the payload and the sig:

    https://somesite.com/sso?sso=PAYLOAD&sig=SIG

claireDMT
  • 9
  • 2