Cross domain session sharing in Rails - Part 2
Tags: rails
Published: 2019-11-07 11:55:17

---
title: Cross domain session sharing in Rails - Part 2
published: true
date: 2019-11-05 04:14:05 UTC
tags: rails
canonical_url: https://prathamesh.tech/2019/11/05/cross-domain-session-sharing-in-rails-part-2/
---

We have multiple micro services at [Memory.ai](https://prathamesh.tech/2019/10/14/cross-domain-session-sharing-in-rails-part-one/Memory.ai) which talk to each other and share data. As we are extracting more and more micro services, we were facing the problem of adding authentication layer in every service. In an ideal world, only one service would perform the authentication and other services will just delegate the authentication to that service.

In the [first part of this blog series](https://dev.to/prathamesh/cross-domain-session-sharing-in-rails-part-1-17om), we saw how Rails and Devise use cookies and session to authenticate the user. In this post, we will see how to share the cookies and session between multiple Rails apps.

Earlier our authentication logic was part of `app.kittens.io`. But when we started working on a new app - `dev.kittens.io`, we didn't want to duplicate the effort for authentication again. More-so, we didn't want the users to sign up again on the new app that we were building. So we decided to extract the authentication into a separate app: `auth.kittens.io`.

### What we want

We have two apps: `app.kittens.io` and `dev.kittens.io`. We want to make sure that once the user is authenticated for `app.kittens.io` they are also authenticated for `dev.kittens.io` and vice-versa.

We use Redis as our session store in the `app.kittens.io`. To solve this problem of sharing the authentication logic across multiple Rails app, we decided to use same Redis as session store for all the new Rails applications. So once the session for a user is written on `auth.kittens.io`, we just had to make sure that same Redis session is found out when the user navigates to `dev.kittens.io` or `app.kittens.io`.

In a trivial workflow, the user session is stored in the session store with a unique identifier. This identifier is actually the value of the session cookie which is shared between the browser and the server. The browser sends this session cookie for every request to the server and the server looks for the session for this key in the session store. You can see an example of such session cookie below.

<!--kg-card-begin: image-->

![](https://prathamesh.tech/content/images/2019/11/image.png)

<!--kg-card-end: image-->

When the user is authenticated using Devise, the session is updated with the user details. For eg. in a normal case, following details will be stored in the session:

<!--kg-card-begin: code-->
```
    {
     "_csrf_token" => 'bk+V3AGLrt0JSQpdIa7ksLgDJsGRmWNZ3y8LJ1j0kuM=',
     "warden.user.user.key" => [1, '$2a$11$YRhQFL.rLh5SRn.dpKqCk.']
    }
```
<!--kg-card-end: code-->

When next time a request comes to the server from the browser, Devise finds out this same session from the session store and declares that the user is already logged in.

So now our problem is reduced to just sharing the cookie between two Rails apps `app.kittens.io` and `dev.kittens.io`.

### Session Cookies in Rails

The session cookies are set against the request host. So in our case, the session cookie set by `auth.kittens.io` is set against `auth.kittens.io` domain. So it is not available on `app.kittens.io` or `dev.kittens.io`. Instead we want to set the cookie on `.kittens.io` domain so that it is available on all of the subdomains of `kittens.io`

We need to change the session store configuration to below to make this happen.

<!--kg-card-begin: code-->
```
Rails.application.config.session_store :redis_session_store,
  key: '_kittens_session',
  serializer: :json,
  domain: :all,
  redis: {
    expire_after: 1.week,
    key_prefix: 'kittens:session:',
    url: ENV['REDIS_SESSIONS_URL']
}

```
<!--kg-card-end: code-->

Here we have modified the session store configuration and added following options:

<!--kg-card-begin: code-->
```
domain: :all
```
<!--kg-card-end: code-->

When `domain: :all` option is specified, Rails derives the actual domain by removing the subdomains from the request host and then sets the cookie against it. Otherwise it sets the cookie against request.host. Additionally, Rails also adds `.` prefix to the root domain before setting the cookie if `domain: :all` option is specified. This makes the cookies set against `.kittens.io` in our case.

> Additionally Rails also allows option `tld_length` which can restrict deriving the subdomains to a number. If you have only two level subdomains like `auth.kittens.io` or `dev.kittens.io` you can pass `tld_length: 2` in the session store configuration. This option is only used by Rails when you use `domain: :all`. In case, we don't pass it, subdomains such as `www.auth.kittens.io` are also mapped to `kittens.io`.

Such cookie is now accessible to the all the subdomains of `kittens.io` so the Rails apps hosting `app.kittens.io` and `dev.kittens.io` can access the cookie and can look up the Redis session store to see if the session for the user to whom this cookie belongs, exists or not. The lookup works automatically when we are using Devise in all the Rails applications. So the user login works automagically on other Rails apps.

This change allowed us not repeat the authentication in all the new apps that we are building. It also allowed to scale the users and authentication service independently.

<!--kg-card-begin: hr-->
* * *
<!--kg-card-end: hr-->

[Subscribe to my newsletter](https://prathamesh.tech/mailing-list) to know more about Rails internals and get to know real world experience reports of using Rails out in the field.