Discourse SSO Logout


(Dean Taylor) #2

Under Settings > Users you should find a setting called logout redirect:

I believe this is what you are asking for.


(Benjamin Boyle) #3

Hi Dean,

Thanks that helps a lot. It solves the first half of the question.

Do you have any ideas about the second half of the question … providing some sort of hook in the forum so that when the user logs out from the main system we can redirect to their browser to a link on the forum that also logs them out of the forum … something like this:

  1. User, on main website, clicks logout.
  2. Main site performs logout (deleting cookies) then redirects to forum’s special logout link.
  3. Login performs logout (deleting cookies) then redirects to the “logout redirect” url

Just need a url in the forum that will help with step 3.


(Dean Taylor) #4

I believe you can make a server-side API call to:
http://discourse.example.com/admin/users/example_username/log_out
http://discourse.example.com/admin/users/{user_id}/log_out
Where the user_id is a number.

This will logout all active discourse user sessions for example_username - obviously double check this works as expected for you.

The same as other Discourse API calls pass the “API Key” as needed:
http://discourse.example.com/admin/api


Is there a POST or GET /logout URL?
Cookie Based Authentication in discourse via Plugin
How to obtain the user_id so I can issue a logout API call
(Adam Capriola) #5

It’s actually /users/id and not /users/username for logging out (last I checked username doesn’t work).


(Abdul Munim Zahid) #6

I know for a fact this is the correct url: /admin/users/{USER_ID}/log_out
You can easily make a POST API call.


(Dean Taylor) #7

I have tested the API and confirmed correct endpoint users a user_id as a number and updated my post above.

The detail of which is mentioned in a post here:
Official Single-Sign-On for Discourse (sso)


(Sam Stickland) #8

How is it proposed to use this logout URL? I see two options:

  • Create a hidden IFRAME to call /admin/users/{USER_ID}/log_out in my sites logout page - seems a little messy
  • Add a redirect option to /admin/users/{USER_ID}/log_out to and bounce the user off this URL as part of my sites logout procedure

S


(UserXtreamz) #9

Hi, for user logout i just tried with the following URL.But it’s giving “[‘BAD CSRF’]”

my URL : /admin/users/{USER_ID}/log_out

instead of USER_ID i given external Id .is it right? or how can i get the user id for user log out


(Kane York) #10

You need to supply admin user authorization to use that endpoint, which you can do by supplying the api_key of an admin user, or the global api_key + an admin api_username.


How to obtain the user_id so I can issue a logout API call
(UserXtreamz) #11

Can you give the example API URL for this?


(UserXtreamz) #12

For enabling the above API .Do we need to do the following steps?.Is it right?


(Sam Stickland) #13

This is how I did it.

Install the discourse-api gem:

You’ll need to monkey-patch it to provide an additional feature, so stick this in one of your initializers (I’ve been meaning to submit a pull request for this, but I haven’t had the time):

module DiscourseApi
  module API
    module Users
      def user_by_external_id(external_id)
        response = get "/users/by-external/#{external_id}"
        response[:body]["user"]
      end
    end
  end
end

Then in ‘lib/discourse.rb’:

module YourApplicationNamespace
  module Discourse

    def Discourse.get_discourse_client
      if ENV['discourse_hostname'] && ENV['dicourse_api_key'] &&
        ENV['discourse_api_username']
        client = DiscourseApi::Client.new(ENV['discourse_hostname'])
        client.api_key = ENV['dicourse_api_key']
        client.api_username = ENV['discourse_api_username']
        client
      else
        nil
      end
    end
  end
end

You need to set discourse_hostname, discourse_api_username and dicourse_api_key as environment variables (or using the dotenv gem).

Then, in a custom sessions controller I did this:

require 'discourse'

class SessionsController < Devise::SessionsController
  def destroy
    begin
      if client = YourApplicationNamespace::Discourse.get_discourse_client
        user = client.user_by_external_id(current_user.id)
        client.log_out(user["id"]) if user
      end
    ensure
      super
    end
  end
end

Which you can tell devise to use by devise_for :users, controllers: { sessions: 'sessions' }. The exception flow is needed to ensure that the user is logged out of devise if the discourse-api gem can’t connect to the forum host.


(eriko) #14

So does this log out every instance of a user or does it logout one particular session. CAS sends logout messages when a user logs out of a session that may been user for multiple applications. CAS also sents logout messages when the users login session expires (10 hour in our cas) so it would be a problem if they where now logged in at home.

I am asking as I want to add support for SLO to my cas_sso app and this gets me closer.


(Sam Stickland) #15

I presume it logs out every instance of a user. There’s no reference to a particular session in the API call so I can’t see how it could do otherwise.


(eriko) #16

That is what I expected. Thanks.


(Seagullmike) #17

I have read this whole thread, and tried to "POST to my sites log_out url
I have included api_key=
do I need any other params?
also, I have my site on a subfolder, none of the above mentions the subfolder in the api, for me I should have:
https://my.site.com/forum/admin/users/2/log_out?api_key=

how can I tell if the api is mounted under /forum?
is there a way for me to have more logging that I can tell what I am missing?


(Ben) #18

Is it possible to delete the cookie? In Chrome i see a cookie called _t, but in AngularJS i can’t see delete this cookie. It’s a Subfolder installation.


(Ben) #19

Here is a code example with NodeJS.

api_key: can be generated in Discourse settings Admin->API
api_username: the Discourse user name of an Admin User

  var request = require('request');
  
  request.post({
    headers: {'content-type' : 'application/x-www-form-urlencoded'},
    url:     'https://discourse.example.com/admin/users/2/log_out',
    body:    "api_username=nameOfaDiscourseAdminUser&api_key=xxxyourMasterApiKeyxxx"
    },function (err, response, body) {
      // if logout is successful the body is {"success":"OK"}
      console.log(body);
  });
  

#20

So, I’m pretty much a discourse newb, but I’m setting up sso from my main site to my discourse site. Now, sign on worked fine out the gate, coding “to spec”, which was nice, but I’m having issues with single sign off.

The short form/focused in version of my issue goes like this. Even with logout_strict on, I can curl the logout api, get a 200 result, and still be logged in :/.

curl -X POST "https://forum.example.com/admin/users/1/log_out?api_username=exampleAdmin&api_key=theKeyFromTheConsole"

(Names changed to protect the guilty, obviously :wink:

This… confuses me? So I was hoping someone could tell me what I’ve overlooked 8).

To verify, I refresh the page I was already logged into, which merrily leaves me where I am. Before, when I had logout_strict on and no sso, I could log out in firefox, for example, and get logged out in chrome as well. I didn’t get auto-relogged in or anything, so I’m expecting the same behavior here. Perhaps incorrectly. I don’t know. Hopefully someone knows better ;).


(Matt Burns) #21

Summary

Handling when user clicks “logout” on discourse:

Set the url to your app’s logout url. eg example.com/logout

Handling when user clicks “logout” on the main site:

You want it to also ensure the user is logged out of discourse right? To do so, make your webserver backend hit this url:

https://discourse.example.com/admin/users/USER_ID/log_out?api_username=DISCOURSE_ADMIN_USERNAME&api_key=DISCOURSE_ADMIN_API_KEY"

Note you have to pass the api credentials of an admin user for it to work.

The extra thing that stung me was the USER_ID has to be the discourse user_id, not the external one your webapp might be more familiar with (it’s what it may have created the SSO user with in the first place). If you want to figure that out you can GET from this url:

https://discourse.example.com/users/by-external/YOUR_EXTERNAL_USER_ID.json?api_key=DISCOURSE_ADMIN_API_KEY&api_username=DISCOURSE_ADMIN_USERNAME

Which will return the json data for that user. Look in {"user":{"id":x}}

Hope that saves someone the time I wasted today :wink: