Part 1 Introduction
What is Single Sign On ? It is an authentication mechanism by which one may use one central authentication system to login into multiple apps, like using one google account to login into “gmail”, “play store”, “google maps” etc.
When and Why to use SSO? When you have multiple apps and you don’t want to burden your users to remember multiple account usernames and passwords(they need free mental space to remember their anniversaries) then one should use SSO. The growing complexity of your system should not be borne by the users, but by the system itself.
I’ll explain a basic functionality of SSO using rails, we’ll make and explore three different systems.
- SSO (runs on localhost:3000)
- Client 1(runs on localhost:3001)
- Client 2 (Almost as same as client 1, an identical twin with different name and a different port,runs on localhost:3002)
For those who directly want to jump to code and see how everything works. Here is the code link, https://github.com/tushartuteja/sso-through-rails-docker.
I have used docker-compose, so running one command “docker-compose up” in the root folder would make the whole system up and running. Secondly you need to run rails db:create and rails db:migrate in SSO container. For those who don’t know what I am talking about, no worries, a 4 minute read on how to use docker with rails for faster development. https://medium.com/@tushartuteja/setting-up-rails-with-postgres-using-docker-426c853e8590
I’ll have code snippets and browser network calls screenshots along the way to make it easier to understand a basic SSO.
Part 2 : How does it work ?
What happens when a user comes to a secure page on client1 ? A secure page is a one that requires authentication.
localhost:3001/home/index in our case
- Client 1 checks if the user is logged in to client 1 or not, this is the local session of client 1. See Image: Application Controller for Client.
- If the user is not logged in, Client 1 will redirect to SSO with a redirect url. In our case redirect url would be (localhost:3001/home/index). The request would look like this. (http://localhost:3000/?redirect_url=http://localhost:3001/home/index) See Video, and Network Log Part 1 image.
- Now SSO would check if the user is logged in SSO or not, this is a global session for us, or local session to SSO. If not it will redirect the user to login-in page. We are using devise so that would internally handle redirect page. See Video, and Network Log Part 1 image.
- After the user logs in to SSO. we redirect back to client 1, with a unique token attached to the url. http://localhost:3001/home/index?token=20da1bc73850338eae6daa76964c4773. See Video, and Network Log Part 2 image.
- This request goes to the client1, client1 uses this token to make a server to server call to fetch the session details pertaining to the token 20da1bc73850338eae6daa76964c4773 and sets the local session of client1.
- Now the user is logged in to client 1 and also sso.
- What happens if the user now goes to client2 (localhost:3002/home/index)
- It checks for local session in client2, since none exist it goes to SSO, but this time user is logged in to SSO, so SSO without asking for user details redirects back to client2 with a new unique token. and repeats the step 5 for client2. See Video, and Network Log Part 3 image.
Lets see this in action.
The above is the browser network log, as soon as I go to localhost:3001, it redirects me to SSO, SSO checks the user is not logged hence it is redirected to users sign_in page. That is what we observe in the above Video.
After we log in we see the following browser network log.
We are signed in through a post request, and redirected to localhost:3000 with a redirect url, this time we are logged in to SSO, hence we go back to our client 1 with the token. Before rendering the 200 response and html document, the client1 verifies the token through a backend request.
Now if we go to Client2, it goes to SSO, SSO has a user logged in, it just redirects back to Client2 with a new Token.
Part 3: Let’s dive into code
The middle-ware ‘authenticated?’ is pretty straight forward. Check if the user session exists. If not check if the token exists in the url, if not redirect to SSO for authentication. If yes check from SSO if the token is valid, if it is valid set the user session.
This is what home controller looks like for client1.
Just checks before the index, if the user is logged in or not through ‘authenticated?’ middle-ware.
SSO code is a little more complex, as it needs to handle authentication, remember usernames and passwords. We are using devise for managing users.
It needs to redirect back to the client after creating a new token and needs to provide an API to validate that token.
The authenticate_user! is a function provided by devise that maintains user session in SSO. SSO has two actions first one is the index where all the requests are redirected to by clients for login. The second one is verify_token that client calls through an API call to get the user session.
Part 4: Conclusion
This is a very basic setup of SSO, please don’t use this in production. It has some pitfals, one noticeable easily is, when I log out of client1, it only logs me out of client1 and not from SSO and client2, and if I log out from SSO, it doesn’t log me out of client1 and client2. A production grade SSO would be a little more complex, would require another article for that. This is the foundation work for that article.