Developers April 21, 2020

Authentication across sub-domains

rab

"When we try to pick out authentication by itself, we find it hitched to everything else in the Universe."

What's the advice for achieving authentication across sub-domains?

auth.example.com
app.example.com

I would like users to authentication using auth.example.comand user data to be accessible at app.example.com.

Assume a web app with no shared database between the two apps.

My thinking thus far is that auth.example.comcould pass a session id to the main app through the user agent and the main app then uses that session id as proof of identity when making an api call to auth.example.com.

  1. 4

    Your system is more like microservices (Separate apps that work together).
    I highly recommend that use JWT because it's stateless.
    Watch this to find more: https://youtu.be/SLc3cTlypwM
    This is a comprehensive video about this problem.
    Also, use subject claim if you want to issue a token for only some services (for example only for app.example.com)

    1. 2

      Hi.

      Great video! Thanks for pointing me there. Turns out the presenter Brian Pontarelli is CEO of FusionAuth.

      Reading elsewhere some have taken JWTs to be a long-lived alternative to session ids. But within the video JWTs are used to pass authentication claims to micro-services and are short-lived.

      Some of my take-aways:

      • JWTs used to reduce chatter between microservices.
      • JWTs are short-lived (minutes).
      • Refresh Tokens are long-lived (days, weeks).

      Thinking out loud here but although we talk about JWTs (which are cool) it may help to talk about the different types of token involved (JWT is simply a representation).

      Refresh Token:

      Generated by (and stored on) the authentication service. Presented to the authentication service to gain (refresh) an Access Token. Video suggested a long random string.

      Refresh Token sounds a lot like a session id in disguise.

      Access Token:

      Contains user details (claims). Generated by authentication service. Stored on user agent. Passed from user agent to microservice. Microservices can trust JWT Access Tokens due to signing. Implies shared secret or public/private key.

      If the TTL for an Access Token is small (e.g. minutes) then it will need refreshing frequently (e.g. minutes).

      Access Token caches user authentication details on the user agent.

      If the above is valid then the authentication service is stateful (due to the Refresh Token) but the other microservices are stateless.

      1. 2

        Awesome!
        About tokens lifetime there are some different methods that you can read here:
        https://www.oauth.com/oauth2-servers/access-tokens/access-token-lifetime/
        Also, the authentication service can be stateless if you don't need a logout. so if you use JWT for both access token and refresh token, you can ignore storing them and only verify the sign part and expiration time (not suggested).
        Also, I suggest reading about kid in the JWT header for security.

        1. 1

          Nice. I can see how single JWT used for both access & refresh gives stateless auth server.

          For microservices running on a common parent domain I assume:

          • JWT passed to services as cookie.
          • Services redirect through auth server if need to refresh cookie (could have non-expiring token).
          • Logout equals deleting cookie.

          Will lookup kid. Cheers.

          1. 2

            I didn't catch the second bullet!
            Is it about refreshing token? refreshing the token is the client's responsibility.

            1. 1

              I was assuming refresh logic is on auth server but client services could refresh token as you say. I think I'm there. Stateless auth across sub-domains using JWT. Many thanks for your help. Much appreciated.

  2. 3

    For imprint.to, I'm storing a session id throughout all subdomains using a site wide cookie by specifying the domain field of the cookie. That should take care of the rest, and you can then make requests using that session id from any subdomain.

    1. 1

      Hi @CrossoverTime, can you please elaborate a bit more? I'm looking to set up something similar with 3 parts:

      • ExpressJS REST API - This is where authentication happens (Passport.js), it returns a JWT token, no sessions here, it's stateless
      • Express JS Web app (website, under let's say example.com) - It calls the API, gets the JWT token, and also persists the session via Redis. Here there is a connect.sid cookie, where the session ID is stored, courtesy of express-session. I can set up this cookie to be cross-domain, using the "." notation in front of the domain (e.g. .example.com)
      • ReactJS web app (under let's say app.example.com) - If a user is authenticated on the main website (Express web app), he/she should be authenticated here as well. I could read the session.id cookie here, but then I would have to call the web app, to get the session, and from there, the user details from the API.

      Doesn't sound right to me, what am I missing? Thanks in advance!

    2. 1

      Cool. That's the thinking (site wide cookie). Thanks for you help. Hope imprint.to is working out for you.

  3. 3

    That works, and in fact I believe what you suggested is not uncommon. I used something like that in my product https://www.indiehackers.com/product/qokka-blockchain with some slight differences: I have one subdomain handles all API requests, where other subdomains are simply client-side apps (e.g. React)

    1. 1

      Hi @qokka, can you please elaborate a bit more on how you handle this? I have explained what I'm trying to achieve in my other comment on this page. Thanks in advance!

      1. 1

        Based on your other comments, I am not sure whether your "ReactJS web app" is a purely client-based app (i.e. can be statically hosted), and if so, it should not handle any authentication or storing any session id. The react client should be authenticating against (the subdomain of) the express (server) JS app, which is the only server handling session stores and ids. You could use the express server as authentication wall only, that gives you tokens to access the APIs, if the APIs are hosted separately.

        1. 1

          Thanks for your response! The React.js app (app.example.com) will be statically hosted (it's a SPA). It won't handle authentication, only the Express JS web app (example.com) will do that (as you rightly said) via calling the API (api.example.com).

          The API returns a JWT to the web app (example.com), and the web app then has a cookie with the session ID. So, the question is, what's the best way to allow the React.js app to be aware of whether a user has logged in already on the web app?

          1. 2

            Setting up a REST call on example.com which checks whether cookies are already present? In Express it should be just checking against req.user. You can store your JWT token in user's session as well.

            1. 1

              Good approach. I was thinking of something very similar as well. This could definitely work. Thanks for your help, really appreciate it!

    2. 1

      Great. Thanks for taking the time to reassure me it is a sensible route. Best of luck with your product.

  4. 2

    While using a cookie for authentication you will encounter a CORS problem. You need to setup the Access-Control-Allow-* headers to achieve this.

    I'm not sure that setup is possible with cookies since the only place you will be able to send the token to, is the domain that created it (being in this case auth.example.com) and not any other place (for example api.example.com).
    Possible solutions:

    • Originate the token from the same place that will need it in the future (ie. api.example.com)
    • Don't use cookies and rely on saving the token somewhere like local storage.

    Both have some security implications but in order to mitigate them, be sure to be using JWT in either case.

    -------
    Further information about how to do the cookies approach is available in https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

    1. 1

      Thank you.

      Cookies can be set "site wide" (as per a reply to this OP) so sent on all sub-domains of ‘.example.com‘.

      CORS is a good point. Not sure why CORS is an all or nothing setting but could run open api and requests passing an invalid session id (via the 'site wide' cookie) fail.

      JWT seems fine and dandy but implies all backends have a secret key to validate and handle encryption for sensitive data. Which is doable but needs care.

      Some talk about payload overhead with JWT as it is passed every request, which implies JWT should be kept small, so suggestion is just use user id, but then we seem we get back to the same problem, and may as well use a session id.

  5. 2

    I've used Auth0 to do this in applications that stretch across domains. To have auth.example.com handle all your authentication, you set it up as a custom domain in Auth0.

    Depending on how you built your app(s) you can use auth0-spa-js for SPAs or Lock for Web.

    Auth0 is really nice because you can easily add other types of authentication like social or the more obtuse enterprise providers like LDAP and Okta. You can also get into defining multiple APIs and roles/permissions for each.

    https://auth0.com/docs/quickstarts

    1. 1

      Thanks for your help. Auth0 looks like the swiss army knife of authentication when I'm looking for a scalpel. How long did it take to navigate the docs and integrate Auth0 the first time? Are you completely happy with the solution it gave you?

      1. 2

        It's definitely not a scalpel and its flexibility can be a tough barrier to pierce through to figure out exactly what you need. I've built 4 production apps with it now so I've been up and down the stack a few times and know my way around.

        I am completely happy with it but realize there is a steep learning curve.

        One intriguing option that I'm dying to play with is https://magic.link/ Which is a passwordless system. It's still quite new but maybe that is an option?

        1. 1

          Thanks for info. Am a fan of passwordless having accepted "passwords are broken". The authentication I will offer is passwordless + social (with those who love their passwords having to use them via a social option) but that's a way to authenticate rather than share.

Recommended Posts