Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

getSession() on API route returns null when also using getSession() in middleware on Vercel #1085

Closed
7 tasks done
babysteps opened this issue Feb 27, 2023 · 10 comments
Closed
7 tasks done
Labels
needs investigation This needs to be investigated further before proceeding

Comments

@babysteps
Copy link

babysteps commented Feb 27, 2023

Checklist

  • The issue can be reproduced in the sample app (or N/A).
  • I have looked into the README and have not found a suitable solution or answer.
  • I have looked into the examples and have not found a suitable solution or answer.
  • I have looked into the API documentation and have not found a suitable solution or answer.
  • I have searched the issues and have not found a suitable solution or answer.
  • I have searched the Auth0 Community forums and have not found a suitable solution or answer.
  • I agree to the terms within the Auth0 Code of Conduct.

Description

@adamjmcgrath My application is using getSession() both in middleware, and also on the server on an API route.
When using v2 of the SDK I am getting a null value returned from getSession() in my API route. Couple interesting things to note:

  1. I do not have this issue using v2.0.0-beta.2. It seems to have begun w/ v 2.0.0. I am currently using 2.2.1 and still have the issue.
  2. I do not have this issue locally. It only happens when deployed to Vercel.
  3. If I comment out my getSession() check in the middleware, the getSession() response in my API call works fine.
  4. I am logging out the cookies in my API code on the server, and can see that the cookies exist prior to calling getSession(), but once getSession() is called, I get the null response and the cookies are deleted.
  5. I am not using any wrapper functions from the SDK. I am only using getSession() in middleware and getSession() and getAccessToken() in my API code.
  6. The API route is not going through middleware. you will see it excluded in the matcher regex.

So the flow is that the user goes to the site, middleware checks their auth, if they are authed they are delivered the page. If not, they are redirected to login. Once the user is logged in and on the site, the API route is called periodically from the client using useSWR()

Below you will see the code for my middleware and for my API route.

Thanks in advance for your help. Been banging my head again the wall for a while with this one.

Reproduction

middleware.js

import { NextResponse } from 'next/server'
import { getSession } from '@auth0/nextjs-auth0/edge';
import {constants} from './config';

export default async function middleware(req) {
    const res = NextResponse.next();

    const {pathname} = new URL(req.url);

    const auth0Session = await getSession(req, res);

    if(!auth0Session && pathname !== '/'){
        return NextResponse.redirect(new URL(`/api/auth/login?returnTo=${req.url}`, req.url))
    }
    if(auth0Session){
        if(!auth0Session.user['https://xyz.com/user_metadata'].firstname && pathname !== '/account/profile' && pathname !== '/account/subscription'){
            return NextResponse.redirect(new URL(`/account/profile`, req.url))
        }
        if (pathname === '/') {
            return NextResponse.redirect(new URL(`/discover`, req.url))
        }
    }
    return res;
}

export const config = {
    matcher: ['/((?!.*\\.|api/auth|about|well-known|favicons|fonts|images|.*.ico|.*.xml|.*.txt|manifest.json).*)'],
}
  • Notice that I am excluding the /api/auth routes from the middleware, so it is not being run when the call to the API is made.

/pages/api/auth/session.js

import {getSiteSession} from "../../../helpers/session/get-session";

const session = async (req, res) => {
    if (req.method === 'GET') {
        const {session} = await getSiteSession(req, res);
        res.json({
            status: 200,
            message: '',
            session
        });
    } else {
        res.json({
            status: 200,
            message: 'Method not supported'
        });
    }
}

export default session;

getSiteSession.js - called from /api/auth/session

import {getAccessToken, getSession} from '@auth0/nextjs-auth0';
import getConfig from "next/config";

const { publicRuntimeConfig, serverRuntimeConfig } = getConfig();

async function getSiteSession(req, res) {
    let auth0Session;

    try {
        console.log('cookies 1', req.cookies['appSession.1']);
        console.log('cookies 0', req.cookies['appSession.0']);
        auth0Session = await getSession(req, res);
        console.log('auth0Session', auth0Session);
    } catch(e){
        console.log(e);
        try {
            await getAccessToken(req, res, {refresh: true});
            auth0Session = await getSession(req, res);
        } catch(e) {
            console.log(e);
            auth0Session = null;
        }
    }

    if (auth0Session) {
        try {
        
                .... do stuff here

        } catch (e) {
            console.log(e);
            auth0Session = null;
        }
    }

    return {
        session: {
            user: {
                isAuthenticated: !!auth0Session
            }
        }
    }
}

module.exports = {
    getSiteSession
};

SDK version

2.2.1

Next.js version

13.2.0

Node.js version

18.x

@adamjmcgrath
Copy link
Contributor

Hi @babysteps - thanks for raising this

I see you're logging multiple cookies (appSession.0 and appSession.1) - do you also get this issue when the session is smaller and not split into chunks?

I'm asking because there's been a couple of issues recently with Next.js and multiple cookies vercel/next.js#38302

Also, do you have a HAR file (with secrets redacted) or a running example I can log into and take a look at?

@adamjmcgrath adamjmcgrath added the needs investigation This needs to be investigated further before proceeding label Feb 28, 2023
@babysteps
Copy link
Author

@adamjmcgrath let me get you an example you can log into. however, in the meantime you bring up an interesting point. i just tried logging in using the version on vercel running fine on v2.0.0-beta.2 and it only creates a single cookie. however, logging in with the same user on a different instance running v2.2.1 is creating the double cookie. all of my AUTH env vars are the same for both instances (except for the base url), and obviously the user data is the same, so maybe there is also a clue in that? it does seem reasonable that the double cookie is the issue, but i can't understand why the same user would create different sized cookies.

please email me [email protected] so that i can send credentials.

@babysteps
Copy link
Author

@adamjmcgrath i just a little more testing around this and found that when logging in with v2.2.1 it is including the idToken, where v2.0.0-beta.2 is not. i seem to remember seeing somewhere in the docs that the default is now to NOT include the idToken, however, that doesn't seem to be correct. i'll keep digging through the docs to find where i saw that, but removing the idToken will likely bring my cookie size down.

@babysteps
Copy link
Author

@adamjmcgrath i found it here #809. i added the env var and now i don't get a giant cookie, and it works on vercel. fyi, you should add that var to the docs https://auth0.github.io/nextjs-auth0/types/config.ConfigParameters.html

technically, this is still an issue, and i looked at that link to vercel that you posted. it has had this issue for a VERY long time.... sure glad i don't need the idToken (for now...)

lastly, while i was just testing, i noticed that getAccessToken(req, res, {refresh: true}); is still returning the idToken in the cookie even though the env var is set to false and it doesn't return an idToken in getSession().

@lostinpatterns
Copy link

@babysteps nice find! i've been getting 404s for /api/auth/login in my console on vercel (had multiple cookies as well) and couldn't figure out why until i set AUTH0_SESSION_STORE_ID_TOKEN to false (docs indicate should default to false so not sure why i needed to explicitly set it to false)

@adamjmcgrath
Copy link
Contributor

@babysteps - thanks for those replies

and i looked at that link to vercel that you posted. it has had this issue for a VERY long time

I still think it's related to that, I've emailed you and will be able to confirm when I debug it

lastly, while i was just testing, i noticed that getAccessToken(req, res, {refresh: true}); is still returning the idToken in the cookie even though the env var is set to false and it doesn't return an idToken in getSession().

Thanks for catching this, I've raise a PR to fix #1091

@babysteps
Copy link
Author

thanks for looking into everything @adamjmcgrath. i have already added the env var AUTH0_SESSION_STORE_ID_TOKEN and set it to false which has resolved my issues (with the exception of the issue you have raised a PR for)

at this point, i don't know that you will be able to gain much from testing my app. since w/ the single cookie there are now no issues. i have done extensive testing trying to resolve this issue and it pretty clearly points to the "double cookie issue". without any other changes, switching between single cookie and double cookie (by disabling/enabling the idToken) shows that it works/doesn't work with only that difference. happy to keep debugging this with you, but from my POV it's pretty clear what the culprit is.

@adamjmcgrath
Copy link
Contributor

Thanks @babysteps

Yep, Next.js have a new issue for this vercel/next.js#46579 which I'm sure is the culprit. Will close this in favour of that.

@wasifhassan101
Copy link

Hello! This is pretty late, but i had a similar issue recently. Everything was fine when running locally, but on Vercel, my first api call was successful with getSession being able to get the user (because the cookie exists before this call) but subsequent calls failed with getSession returning null (because the cookie was lost/deleted). I am not using getSession in my APIs but only in the middleware.

The res from the getSession line has cookies and set-cookie headers.

On inspection I found that on Vercel, and I'm assuming getSession works by relying on cookies, using NextResponse.next() meant that the ResponseCookies was empty and there was no set-cookies header. I believe what's happening is that not utilising the res and passing it to NextResponse.next() either means that previous cookie is lost or overwritten (not sure about that). However, if middleware returns NextResponse.next(res), then the cookie is passed forward to be utilised by newer requests.

The cookie exists prior to calling getSession() but after it the NextResponse being returned without explicitly passing the cookie deletes it probably. So, in your case passing the res to the NextResponse.redirect() might be the solution by explicitly passing the session cookie. This cookie can allow getSession to return the user in your API.

This solved the problem for me.

@cdascal
Copy link

cdascal commented Apr 19, 2024

@wasifhassan101 Any chance you can share an snippet with us? I know it's a long shot, I'm really curious though

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs investigation This needs to be investigated further before proceeding
Projects
None yet
Development

No branches or pull requests

5 participants