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

Calling getSession inside middleware always return { user: null } #115

Open
gmocquet opened this issue Oct 18, 2024 · 12 comments
Open

Calling getSession inside middleware always return { user: null } #115

gmocquet opened this issue Oct 18, 2024 · 12 comments

Comments

@gmocquet
Copy link

Hello,

I'm using the following middleware:

import { authkitMiddleware, getSession } from '@workos-inc/authkit-nextjs';
import { NextRequest, NextFetchEvent, NextResponse } from 'next/server';

async function authMiddleware(request: NextRequest) {
  const response = await authkitMiddleware({
    middlewareAuth: {
      enabled: true,
      unauthenticatedPaths: ['/']
    }})(request);

  const sess = await getSession(response);
  console.log('sess', sess);

  return response;
}

export default async function middleware(request: NextRequest, context: NextFetchEvent) {
  try {
    // This will check if the user is authenticated
    return authMiddleware(request);
  } catch (error) {
    console.error('Error in middleware:', error);
    return NextResponse.json('Internal Server Error', { status: 500 });
  }
}
// Match against pages that require auth
export const config = {
  matcher: [
    // Skip Next.js internals and all static files, unless found in search params
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    // Always run for API routes
    '/(api|trpc)(.*)',
  ],
}

The console.log('sess') always return { user: null }.

On a client comportent app/(landing-page)/page.tsx, I put the following code to get the current user information:

import Hero from '@/components/landing-page/Hero';
import ProductFeatures from '@/components/landing-page/ProductFeatures';
import { withAuth } from '@workos-inc/authkit-nextjs';

export default async function LandingPage() {
  const { user } = await withAuth();
  console.log(user);

  return (
    <>
      <Hero />
      <ProductFeatures />
    </>
  )
}

This time, I observe the expected behaviour:

  • I got null is the user is NOT logged in
null
  • I got the ùser object` if he is LOGGED IN
{
  object: 'user',
  id: 'user_xxxx',
  email: 'xxx',
  emailVerified: true,
  firstName: 'xxx',
  profilePictureUrl: null,
  lastName: 'xxx',
  createdAt: '2024-10-18T08:40:52.318Z',
  updatedAt: '2024-10-18T10:09:59.626Z'
}```

I'm using node v20.17.0
my OS is macOS 15.0.1 (Apple Silicon chip)

Bellow, my package.json
[package.json](https://github.com/user-attachments/files/17435982/package.json)

Thx for your help
@PaulAsjes
Copy link
Collaborator

Hi there, would you mind enabling debug mode in your middleware and posting the logs?

@wspurgin
Copy link

@PaulAsjes late to the party, but I'm facing the same error - just doing the default custom middleware example from the readme with a console.log of the session to reproduce OPs example.

With debug on, the only log I see is:
Session is valid

But getSession always yields a { user: null }

@PaulAsjes
Copy link
Collaborator

Where are you calling getSession? Is it in the middleware or on a page route?

@wspurgin
Copy link

wspurgin commented Dec 2, 2024

@PaulAsjes as part of the middleware per the boilerplate example.

@PaulAsjes
Copy link
Collaborator

If in the middleware, be sure to pass in a response object to getSession. Otherwise the method will attempt to read the cookie from the request (which may not exist) instead of the response where it might have just been set:

const response = await authkitMiddleware()(request, event);

const session = await getSession(response);

@wspurgin
Copy link

wspurgin commented Dec 2, 2024

const session = await getSession(response); is verbatim what's giving sess { user: null } even when I am authenticated and a cookie exists. Getting the session info directly from the cookie will provide the expected results when a cookie exists. getSession does not however.

Here's the full example which I made just combining the two examples in the README - then added the cookie parsing when user was always null.

  // authkitMiddleware will handle refreshing the session if the access token has expired
  const response = await authkitMiddleware({
    debug: true,
    redirectUri: `${process.env.FRONTEND_URL}/callback`,
    middlewareAuth: {
      enabled: true,
      unauthenticatedPaths: [],
    },
  })(request, event);


  // If session is undefined, the user is not authenticated
  const session = await getSession(response);
  console.log("sess", session);

  const cookieStore = await cookies();
  const authCookie = await cookieStore.get('wos-session');

  if (!authCookie?.value) {
    // If the cookie is not present, the user is not authenticated
    // and we should return the workos Authkit response
    return response;
  }

  const workos = new WorkOS(process.env.WORKOS_API_KEY ?? '');
  const cookieSession = await workos.userManagement.getSessionFromCookie({
    sessionData: authCookie.value,
    cookiePassword: process.env.WORKOS_COOKIE_PASSWORD,
  });
  console.log("session from cookie", cookieSession);

  return response;

getSession(response) logs yield:
sess { user: null } every time - with or without a cookie set (e.g., on initial sign in)
and when a cookie is set, manually parsing the cookie via getSessionFromCookie yields:

session from cookie {
  accessToken: '....',
  refreshToken: '...',
  user: {
  object: 'user',
  id: 'user_....',
  email: '.....',
  emailVerified: true,
  firstName: 'Me',
  profilePictureUrl: null,
  lastName: 'Test',
  createdAt: '....',
  updatedAt: '......'
  }

@overra
Copy link

overra commented Dec 5, 2024

I ran into a similar issue with the session user being null.

In my case I think I was trying to access the session user on a path that was included in the unauthenticatedPaths array, and when the middleware ran on a request to that path it didn't process the cookies/session.

I was also trying to setup the middleware so that / would render the dashboard when logged in, and the marketing home page otherwise. That might have added to my confusion.

FYI a short version of the app structure

/(marketing)/layout.tsx
/(marketing)/home/page.tsx
/(app)/layout.tsx
/(app)/page.tsx

and the middleware function looks like

export default async function middleware(
  request: NextRequest,
  event: NextFetchEvent
) {
  const url = request.nextUrl;

  const response = await authkitMiddleware({
    middlewareAuth: {
      enabled: true,
      unauthenticatedPaths: [
        "/home",
      ],
    },
  })(request, event);

  const session = await getSession(response as NextResponse);

  if (!session?.user && url.pathname === "/") {
    return NextResponse.rewrite(new URL("/home", request.url));
  }

  return response;
}

@overra
Copy link

overra commented Dec 5, 2024

Immediately after posting this I went back to the project and things stopped working. After a little more time and looking at the source code of authkit-nextjs, I found that the response passed to getSession() was optional and it falls back to nextCookies which is the properly awaited cookies() call for Next.js 15.

I ran into a similar issue with the session user being null.

In my case I think I was trying to access the session user on a path that was included in the unauthenticatedPaths array, and when the middleware ran on a request to that path it didn't process the cookies/session.

I was also trying to setup the middleware so that / would render the dashboard when logged in, and the marketing home page otherwise. That might have added to my confusion.

FYI a short version of the app structure

/(marketing)/layout.tsx
/(marketing)/home/page.tsx
/(app)/layout.tsx
/(app)/page.tsx

and the middleware function looks like

export default async function middleware(
  request: NextRequest,
  event: NextFetchEvent
) {
  const url = request.nextUrl;

  const response = await authkitMiddleware({
    middlewareAuth: {
      enabled: true,
      unauthenticatedPaths: [
        "/home",
      ],
    },
  })(request, event);

  const session = await getSession(response as NextResponse);

  if (!session?.user && url.pathname === "/") {
    return NextResponse.rewrite(new URL("/home", request.url));
  }

  return response;
}

@kevinmitch14
Copy link

kevinmitch14 commented Dec 11, 2024

Having exact same issue, following example in readme does not work. Debug logs will say there is a session, but when checking getSession(), there is not a session. This is making it impossible to do anything extra with the session, check org, permissions etc. before returning a response

@sergesteban
Copy link

I was facing the same error, yet i noticed that not passing response on getSession made it work. I'm using "next": "^15.0.1",

async function authMiddleware(request: NextRequest, context: NextFetchEvent) {
  const middleware: NextMiddleware = await authkitMiddleware({
    debug: true,
    middlewareAuth: {
      enabled:  isDev() ,
      unauthenticatedPaths: [
        "/"
      ],
    },
  })
  const response = await middleware(request, context) as NextResponse
  const session = await getSession(response);
  console.log('authMiddleware.session', session);
  return response;
}

logs with response as parameter

Session is valid
authMiddleware.session { user: null }

quick update based on @overra comment

const response = await middleware(request, context) as NextResponse
const session = await getSession();
console.log('authMiddleware.session', session);
return response;

logs

authMiddleware.session { sessionId: '...',  ... }

Immediately after posting this I went back to the project and things stopped working. After a little more time and looking at the source code of authkit-nextjs, I found that the response passed to getSession() was optional and it falls back to nextCookies which is the properly awaited cookies() call for Next.js 15.

I ran into a similar issue with the session user being null.
In my case I think I was trying to access the session user on a path that was included in the unauthenticatedPaths array, and when the middleware ran on a request to that path it didn't process the cookies/session.
I was also trying to setup the middleware so that / would render the dashboard when logged in, and the marketing home page otherwise. That might have added to my confusion.
FYI a short version of the app structure

/(marketing)/layout.tsx
/(marketing)/home/page.tsx
/(app)/layout.tsx
/(app)/page.tsx

and the middleware function looks like

export default async function middleware(
  request: NextRequest,
  event: NextFetchEvent
) {
  const url = request.nextUrl;

  const response = await authkitMiddleware({
    middlewareAuth: {
      enabled: true,
      unauthenticatedPaths: [
        "/home",
      ],
    },
  })(request, event);

  const session = await getSession(response as NextResponse);

  if (!session?.user && url.pathname === "/") {
    return NextResponse.rewrite(new URL("/home", request.url));
  }

  return response;
}

@monolithed
Copy link

Has anyone managed to find a workaround? How do you live without authorization for several months...

@sergesteban
Copy link

Has anyone managed to find a workaround? How do you live without authorization for several months...

in the comments you'll find the workaround. there's no need to live without auth.

const session = await getSession();

instead of

  const session = await getSession(response);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

7 participants