Rails 4: Single-Sign-Out of all applications with Doorkeeper
This is a quick solution to add single-sign-out of all applications for a doorkeeper based oauth2 application.
For my latest project I created a separate login-app (server app running under a subdomain login.example.com) and client app (running under another subdomain client.example.com) using it for authentication. The goal is to use one login service for several apps, keeping user registration and related stuff in one single place.
I’ve chosen the doorkeeper gem for the server app and devise using oauth2 for the client app. How to set things up can be read from the example apps doorkeeper-gem provides.
The login process runs very smooth, but as soon as it comes to logout, it’s not nice anymore. Imagine the following behaviour:
- A user requests client.example.com and clicks on a link labeled “Login” which calls
https://client.example.com/users/auth/mystrategy
- The browser redirects him to
https://login.example.com/users/sign_in
where he can enter his credentials and finally - gets redirected to
https://client.example.com
again - After doing nasty stuff on the page he clicks on the link labeled “Logout”,
which basically calls
DELETE https://client.example.com/sign_out
.
What this does is simply removing the Cookie from the subdomain client app, logging out the user, so that someone else might login, right? Well, partially yes, but… no – let’s see what happens:
- The second user clicks on the link labeled “Login” and gets redirected to
https://login.example.com/users/sign_in
… - …and immediately redirected back to the client app, logged in as the first user. WTF?
Of course, because only the client app killed the session cookie. The login app, when redirected to, finds the login session cookie, finds a valid auth_token and signs in the user.
But I want to sign in as another user!
Unfortunately there’s no easy way (I could find) to sign out the user from the login app as well, because
- you can’t simply call
DELETE https://login.example.cm/users/sign_out
because you don’t have the authenticity_token - you could try adding a special controller to login app, authorize yourself with the token stored in the client app and…
but there’s a simpler solution to this which is basically not logging out the user when he wants to, but always immediately after he signed in.
The first solution found in the net was to set resource_owner_from_credentials
inside doorkeepers initializer,
logging out the user at the end. However, this didn’t work for me for whatever reasons – the block was never called.
So I simply added a doorkeeper authorizations controller to the app doing the logout.
use_doorkeeper do
controllers applications: 'doorkeeper', authorizations: 'authorizations'
end
The applications controller in here is part of my implementation of the app itself (so not needed for this tweak), but the new stuff is the authorizations controller.
class AuthorizationsController < Doorkeeper::AuthorizationsController
def new
super
env['warden'].logout
end
end
Et voilà – small but effective. Now you can log in, get redirected to the client app with a valid token and logging out of the client app will bring up the login page again.
Ein Kommentar