Recommended approach to share session information between applications on subdomains
See original GitHub issueQuestion š¬
Hello! First of all, thanks for all the great work on this amazing library!
Iām trying to implement a solution in which i have the same authentication on subdomains as well as on main domain. What is the recommended approach to share session information between all applications?
This is a continuation of a discussion that started on some previous issues, like #405, #794, #1668. All those issues are now closed without a full example or definitive answer, so i decided to make a summary of what was previously discussed, in hope that i can get some help on deciding what is the best approach to do that integration on all domains and subdomains of my application and help document that approach for others in the same need.
How to reproduce āļø
The application iām working on is, actually, an ecosystem of applications, and not all of them are made with Next.js (or even with React). Many are legacy applications written in JSP.
The main one isnāt an Next.js app and will live in āwww.my-domain.comā. It will have sub-domains, as in āapp1.my-domain.comā, āapp2.my-domain.comā, etc. As explained in the FAQ:
If you are using a different framework for you website, you can create a website that handles sign in with Next.js and then access those sessions on a website that does not use Next.js as long as the websites are on the same domain.
If you use NextAuth.js on a website with a different subdomain then the rest of your website (e.g. auth.example.com vs www.example.com) you will need to set a custom cookie domain policy for the Session Token cookie.
So, since the main application isnāt a React / Next.js app, I will have to implement an auth application so i can use NextAuth: āauth.my-domain.comā. Notice that itās the ONLY application that includes NextAuth - from my understanding, it makes no sence to add NextAuth to all my other Next.js applications.
I created a diagram to help illustrate the ecosystem:
To set a custom cookie domain policy for the Session Token cookie, i did in my [ā¦nextauth] API route what was originally posted by @Xodarap in https://github.com/nextauthjs/next-auth/issues/405#issuecomment-737593528:
const useSecureCookies = process.env.NEXTAUTH_URL.startsWith('https://')
const cookiePrefix = useSecureCookies ? '__Secure-' : ''
const hostName = Url(process.env.NEXTAUTH_URL).hostname
const options = {
cookies: {
sessionToken:
{
name: `${cookiePrefix}next-auth.session-token`,
options: {
httpOnly: true,
sameSite: 'lax',
path: '/',
secure: useSecureCookies,
domain: hostName == 'localhost' ? hostName : '.' + hostName // add a . in front so that subdomains are included
}
},
},
}
Doing like that, i was successfully able to access the cookie ā__Secure-next-auth.session-tokenā, created by āauth.my-domain.comā and set in the domain ā.my-domain.comā, in all my other applications.
@iaincollins said in https://github.com/nextauthjs/next-auth/issues/405#issuecomment-737814371 that he was evaluating ways to make that configuration easier, but the issue #405 was closed because of inactivity.
As @iaincollins explained in https://github.com/nextauthjs/next-auth/issues/417#issuecomment-656606639:
Session Tokens are typically stored in cookies. To best protect against session hijacking (e.g. from third party JavaScript in advertising, tracking, browser extensions, XSS) these should be server only readable cookies that are not accessible directly from JavaScript. This is the model NextAuth.js uses.
If a user is signed in, any request from the client will have a secure, sever only readable cookie set that can be used to either look up a session in a database (if it is a database session token) or decoded and verified (if it is a JSON Web Token) to identify the user.
Using server readable only cookies is secure without a CSRF token.
So in regards to the protection needed to secure my backend all is done: the backend will receive the cookies together with the requests made by the frontend and decode and verify the identity of the user.
What i still donāt know is what is the best approach to sharing the session information between all the frontend applications. Should i just read the information in the cookie in each of my applications? Because, doing like that, the cookie lifespam wonāt be increased, like happens when i access the āsessionā endpoint or the āuseSessionā / āgetSessionā client. For example:
@iaincollins posted in https://github.com/nextauthjs/next-auth/issues/796#issuecomment-718765049 a quite great explanation of the challanges of defining the maxAge of the JWT. I quote:
By default, a Next.js Single Page App using NextAuth.js will load session once and on subsequent page transitions, the useSession hook will reuse the response it already has.
However, the app wide session state WILL be updated in the SPA if the window loses/regains focus or if the user logs out in another window in the same browser (it uses event hooks to do this) OR anytime you call getSession() in your app. This makes Single Page Apps efficient by default - they donāt check your session on every page navigation, which keeps network traffic down and page responses snappy - but keeps the session state from getting stale by instead re-validating when a page gains focus again.
So i think it would be of great loss if we donāt access the session via the āsessionā endpoint and, as a result, lose on that great functionality provided by NextAuth.
The problem: As @subhendukundu commented in https://github.com/nextauthjs/next-auth/issues/794#issuecomment-875620767, iām also having problens trying to use the endpoints provided by NextAuth on my āauth.my-domain.comā (like āhttps://auth.my-domain.com/api/auth/sessionā) application.
First, i was getting CORS related errors when i tried to consume the endpoints. About those, i get that may be related to the way Next.js protects itās API routes, but i think it is usefull to document the solution here, since it will be a mandatory step for the solution to work. I followed the guide provided in the Vercel documentation and in the Next.js documentation - in the ānext.config.jsā file, i created a āheadersā function:
// next.config.js
// NEXT_PUBLIC_ENV is an environment variable defined on my .env, with values 'development', 'staging' or 'production' in the respective environments.
module.exports = {
async headers() {
// To help with local development...
if (process.env.NEXT_PUBLIC_ENV === 'development') {
return [
{
source: "/api/auth/:path*",
headers: [
{ key: 'Access-Control-Allow-Credentials', value: 'true' },
{ key: 'Access-Control-Allow-Origin', value: '*' },
{ key: 'Access-Control-Allow-Methods', value: 'GET, OPTIONS, PATCH, DELETE, POST, PUT' },
{ key: 'Access-Control-Allow-Headers', value: 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version' },
],
},
];
}
// In the other environments...
return [
// https://vercel.com/support/articles/how-to-enable-cors#enabling-cors-in-a-next.js-app
// https://nextjs.org/docs/api-reference/next.config.js/headers#header-cookie-and-query-matching
{
// matching all auth API routes
source: "/api/auth/:path*",
// if the origin has '.my-domain.com'...
has: [
{ type: 'header', key: 'Origin', value: '(?<serviceName>^https:\/\/.*\.my-domain\.com$)' },
],
// these headers will be applied
headers: [
{ key: 'Access-Control-Allow-Credentials', value: 'true' },
{ key: 'Access-Control-Allow-Origin', value: '*' },
{ key: 'Access-Control-Allow-Methods', value: 'GET, OPTIONS, PATCH, DELETE, POST, PUT' },
{ key: 'Access-Control-Allow-Headers', value: 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version' },
],
},
];
}
};
(edit: the code i previously posted to configure CORS was wrong (i was checking the host of the request, and the correct way to do that is checking the āOriginā header). With that configuration, my applications on my subdomains can access the auth endpoints from my āhttps://auth.my-domain.comā, and it still keeps protected from access made from other domains.
Iām using āfetchā to get the data from the āhttps://auth.my-domain.com/api/auth/sessionā endpoint. Something like that:
const a = await fetch('https://auth.my-domain.com/api/auth/session');
const b = await a.json();
// b will have the session information
If i do that fetch inside my āauth.my-domain.comā application, it works as expected, bringing the session information. The problem is that, if i try to do that same fetch from my āapp2.my-domain.comā, for example, i donāt receive the session information, only an empty object ({}). Why is that happening? Did i do something wrong?
Also, despite all the references i provided, i still donāt know if that is the recommended approach to share the session information between my applications.
Thanks in advance!
Documentation feedback
- Found the documentation helpful
- Found documentation but was incomplete
- Could not find relevant documentation
- Found the example project helpful
- Did not find the example project helpful
Contributing šš½
Yes, I am willing to help answer this question in a PR
Issue Analytics
- State:
- Created 2 years ago
- Reactions:6
- Comments:23 (3 by maintainers)
My system using the NextAuth login implementation goes into production next week or the other, wish me luck kkk After that, i hope to make a guide of the configs i had to do to get all working (i only had to use patch-package do resolve proxy issues i was having inside my corporate proxy, but everything else i was able to accomplish with the NextAuth configuration only).
A minimal reproduction example would be nice š