How to authenticate a user on a domain (with ssl) and redirect and create the session in a different domain? How to authenticate a user on a domain (with ssl) and redirect and create the session in a different domain? heroku heroku

How to authenticate a user on a domain (with ssl) and redirect and create the session in a different domain?


Let's take a look at shopify and how they do it.

Shopify differentiates between two cases:

  • The tenant's domain has SSL
  • The tenant's domain has no SSL

Case #1 - The tenant's domain has SSL

Tenant's fictive domain: https://www.secure.shop

The sign-up form points to https://www.secure.shop/signup and after a successful sign-up I get a 302 Found which redirects me to https://www.secure.shop and sets a session cookie.

-> POST https://www.secure.shop/signup, (signup data)<- 302 Found    Location: https://www.secure.shop   Set-Cookie: _session_id=eba010959d42ec1b734c7bc335ca13cb; path=/;secure; HttpOnly-> GET https://www.secure.shop<- 200 OK

The sign-in form points to https://www.secure.shop/login and after a successful sign-in I get a 302 Found which redirects me to https://www.secure.shop and sets a session cookie.

-> POST https://www.secure.shop/login, (credentials)<- 302 Found    Location: https://www.secure.shop   Set-Cookie: _session_id=238aba8be83ceb3ba4a8ae4d94b1b026; path=/;secure; HttpOnly-> GET https://www.secure.shop<- 200 OK

The check-out happens through https://www.secure.shop/checkout.

The log-out points to https://www.secure.shop/logout and what happens is:

-> GET https://www.secure.shop/logout<- 302 Found   Location: https://www.secure.shop   Set-Cookie: _session_id=3b778bb251e170a9e3b1cd8794862203; path=/;  secure; HttpOnly-> GET https://www.secure.shop<- 200 OK

Conclusion: Everything runs under the tenant's domain. One session cookie, no magic involved.

Case #2: The tenant's domain has no SSL

Tenant's fictive domain: http://www.insecure.shop

The sign-up form points to https://insecureshop.myshopify.com/account and when I create a new account the following happens:

-> POST https://insecureshop.myshopify.com/account, (signup data)<- 302 Found    Location: http://www.insecureshop.com/account?sid=514d3e699fd55ddb7c12398405e65abf   Set-Cookie: _secure_session_id=c31d544b27ee8a49b5a6cf9e303e6829; path=/; secure; HttpOnly-> GET http://www.insecureshop.com/account?sid=514d3e699fd55ddb7c12398405e65abf<- 200 OK    Set-Cookie: _session_id=18d5f07e1e61d8707e111879860abad6; path=/; HttpOnly

The sign-in form points to https://insecureshop.myshopify.com/account/login and what happens is:

-> POST https://insecureshop.myshopify.com/account/login, (credentials)<- 302 Found   Location: http://www.insecure.shop/account?sid=a232e58b8cb9fb4936ddf889ab7e73e4   Set-Cookie: _secure_session_id=d54a653cf4fcb66b831968e9e669b005; path=/; secure; HttpOnly-> GET http://www.insecure.shop/account?sid=a232e58b8cb9fb4936ddf889ab7e73e4<- 200 OK   Set-Cookie: _session_id=44e041cdbcb64d2a2281bb64db52ada0; path=/; HttpOnly

The check-out happens through https://checkout.shopify.com

The log-out points to http://www.insecure.shop/account/logout and what happens is:

-> GET http://www.insecure.shop/account/logout<- 302 Found   Location: https://insecureshop.myshopify.com/account/logout?sid=d6c774d39307def7f772de31031c665c   Set-Cookie: _session_id=8dfb0a130d6f479d1af3a52c40ad3be6; path=/; HttpOnly-> GET https://insecureshop.myshopify.com/account/logout?sid=d6c774d39307def7f772de31031c665c<- 302 Found   Location: http://www.insecure.shop   Set-Cookie: _secure_session_id=18fcde259616586f89831399cc9c2425; path=/; secure; HttpOnly-> GET http://www.insecure.shop<- 200 OK

Conclusion: In the case of an insecure shop, everything is done twice, two separate (and different!) sessions are created once on the tenant's insecure domain and once on myshopify subdomain of the tenant. Both sessions point to the same user record in the back-end.

The credentials are sent encrypted and a single-use token is passed on the redirect to the insecure domain which then creates an authenticated session on the insecure domain. This token is transmitted in plain text.

First thing that pops in your mind is probably:

What if a man-in-the-middle intercepts that token and hijacks the session?

Well, the attacker will be authenticated as you on http://www.insecure.shop, but not on https://insecureshop.myshopify.com.

What if we try to be smart and use an AJAX request with CORS and set the session cookie manually with JavaScript, so no redirect with magic tokens occur

This doesn't help either since the session cookie itself is transmitted in plain text all the time, so the session can be hijacked anyway. Even worse, we would have only one session.

Why are they using two different sessions / sessions ids for the same user?

Here lies the magic, your session on http://www.insecure.shop can be compromised easily, however your session on https://insecureshop.myshopify.com is not compromised since the attacker doesn't know the session ID because the cookie was transmitted by SSL and the session ID is different from the "insecure" one.

But still the attacker can abuse my account on http://www.insecure.shop

True, but what is he able to do? He can add products to your cart, read your profile and so on, but he can't do a check-out and charge your credit card. Why? Because the check-out goes through the secure part at https://insecureshop.myshopify.com, for which he doesn't have the session cookie and thus is unauthenticated.

But if the attacker is smart, he could just change the password and re-login on both the insecure and secure part

Not if you add appropriate measures, i.e require the user to enter his password to do any profile changes. The attacker doesn't know the credentials since they were transmitted by SSL.

Still, isn't there a better solution

There is - use HTTPS everywhere, CloudFlare for examples makes it dead easy to put SSL in front of your customer's domains. This gives both you less overhead to implement and an added value for your customers and your customer's customers. Win win situation.

You don't have to use a third party solution like CloudFlare for this, since you are in charge - all traffic goes through your server / front-end proxy (e.g nginx). You could manage the SSL certificates for your customers and charge them, however this becomes quite cumbersome both for accounting and configuration since each domain needs its own certificate.

UPDATE (important)

Please note the subtle difference in the insecure case, the two cookies have the names _session_id and _secure_session_id. This is for good reason as both sessions exist on the same rails instance and they could be used interchangeably, which is a bad thing. What I think they're doing is to set a flag on the session whether the session was created through a secure channel and add an appropriate before action like

before_action :require_secure_session, only: :checkoutdef require_secure_session  head :unauthorized unless session[:is_secure_flag]end

Sources:

Shop examples were taken from http://wemakewebsites.com/blog/80-best-shopify-stores-for-ecommerce-inspiration, https one was #79, and http one was #23. Tools used: Chrome developer tools (network tab), cURL.


You may find this answer helpful. In the redirect URL or HTTP request to the custom domain you could pass in a token that gets caught by the rails app of the custom domain to log the user in.


I created an app that had a similar functionality. The way I handled this was to have a simple redirection. You can save the subdomain string to the user account when they create the account, then when the user successfully signs in you can redirect them like so

    if current_user.subdomain?      redirect_to root_url(subdomain: current_user.subdomain)    else      redirect_to root_url, notice: "Logged in!"    end