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

Invalid calls to auth() return an object that is not a Session, causing server-rendered useSessions to be status: 'authenticated' when they are not #11934

Open
10hendersonm opened this issue Sep 30, 2024 · 1 comment
Labels
bug Something isn't working triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime.

Comments

@10hendersonm
Copy link

10hendersonm commented Sep 30, 2024

Environment

  System:
    OS: macOS 14.6.1
    CPU: (14) x64 Apple M3 Max
    Memory: 18.59 MB / 36.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.16.0 - ~/.nvm/versions/node/v20.16.0/bin/node
    Yarn: 1.22.21 - ~/.nvm/versions/node/v20.16.0/bin/yarn
    npm: 10.8.1 - ~/.nvm/versions/node/v20.16.0/bin/npm
    pnpm: 9.6.0 - ~/.nvm/versions/node/v20.16.0/bin/pnpm
  Browsers:
    Chrome: 128.0.6613.138
    Safari: 17.6
  npmPackages:
    next: 14.2.5 => 14.2.5 
    react: 18.3.1 => 18.3.1 

(working in a monorepo w/ pnpm, so next and next-auth aren't in the same place)

    @auth/core: 0.31.0 => 0.31.0 
    next-auth: 5.0.0-beta.18 => 5.0.0-beta.18 

Browser: Vivaldi 6.9.3447.46 (Stable channel) (arm64)

Reproduction URL

https://github.com/10hendersonm/next-auth-response-repro

Describe the issue

TL;DR When there is no AUTH_SECRET, auth() returns a not-Session that causes useSession() to say the user is authenticated.

Some unnecessary context:

Part of my job involves maintaining a templated next application which we've added [email protected] to (I duplicated against the current beta using the next-auth-example). I fully expect my users to generate an app from the template and then fire it up absentmindedly without having read my documentation and without having added any .env values beforehand*. In fact, one avenue of creating the app also deploys it straight into a containerized non-production environment where it's off and "running" with no working config.

We do provide some default .env values that get the OAuth provider set up, but they're on their own to generate an AUTH_SECRET. There is code in place that will render an instructional error page on how to apply the necessary .env values, but currently a different component in the app is causing errors when rendered in this state.

It's a weird use case, but I'm actually trying to successfully render content to the "user" (engineer) when their auth is misconfigured.

*Because that's what I would do.


When running an app with no AUTH_SECRET (I know, I know, read above to understand why) the auth() function returned from NextAuth(...) will return a { message: string } rather than a Session or null:

{
    message: 'There was a problem with the server configuration. Check the server logs for more information.'
}

auth()'s typings don't indicate a possibility of this:

auth: ((
...args: [NextApiRequest, NextApiResponse]
) => Promise<Session | null>) &
((...args: []) => Promise<Session | null>) &
((...args: [GetServerSidePropsContext]) => Promise<Session | null>) &

This occurs because the auth function performs a config assertion, which discovers the missing AUTH_SECRET and returns it out as a 500 response with an accompanying status message.

const warningsOrError = assertConfig(internalRequest, config)

if (!options.secret?.length) {
return new MissingSecret("Please define a `secret`")
}

const message =
"There was a problem with the server configuration. Check the server logs for more information."
return Response.json({ message }, { status: 500 })

This { message: string } object evaluates as truthy, causing a <SessionProvider session={auth} {...}> to provide a context value with a status of authenticated to child useSession invocations:

status: loading
? "loading"
: session
? "authenticated"
: "unauthenticated",

const value: SessionContextValue<R> = React.useContext(SessionContext)

useSession() calls invoked from the server (see below before raising pitchforks) will return this value. Note the status: 'authenticated':

{
    data: {
        message: 'There was a problem with the server configuration. Check the server logs for more information.'
    },
    status: 'authenticated',
    update: [AsyncFunction: update]
}

Similar to auth(), the typings for useSession don't indicate this as a possibility:

/**
* useSession() returns an object containing three things: a method called {@link UpdateSession|update}, `data` and `status`.
*/
export type SessionContextValue<R extends boolean = false> = R extends true
?
| { update: UpdateSession; data: Session; status: "authenticated" }
| { update: UpdateSession; data: null; status: "loading" }
:
| { update: UpdateSession; data: Session; status: "authenticated" }
| {
update: UpdateSession
data: null
status: "unauthenticated" | "loading"
}

I understand that useSession is not intended for server-side use, however "Next.js uses the [...] Client Component JavaScript instructions to render HTML for the route on the server." This means calls to useSession can occur on the server even in client components declared explicitly with a 'use client' directive.

How to reproduce

https://github.com/10hendersonm/next-auth-response-repro contains no functional diffs from the next-auth-example, however it does log the auth() and useSession() responses. Run the app locally via pnpm dev / npm run dev / etc.

⚠️ You should deliberately perform this without .env values. ⚠️

Accessing http://localhost:3000/client-example generates this log on the server:

$ pnpm dev

> @ dev /Users/me/development/projects/next-auth-response-repro
> next

  ▲ Next.js 14.2.13
  - Local:        http://localhost:3000

 ✓ Starting...
 ✓ Ready in 2.8s
 ○ Compiling /middleware ...
 ✓ Compiled /middleware in 528ms (454 modules)
[auth][error] MissingSecret: Please define a `secret`. Read more at https://errors.authjs.dev#missingsecret
    at assertConfig (webpack-internal:///(middleware)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@auth/core/lib/utils/assert.js:66:16)
    at Auth (webpack-internal:///(middleware)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@auth/core/index.js:88:95)
 ○ Compiling /client-example ...
 ✓ Compiled /client-example in 3s (1244 modules)
[auth][error] MissingSecret: Please define a `secret`. Read more at https://errors.authjs.dev#missingsecret
    at assertConfig (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@auth/core/lib/utils/assert.js:66:16)
    at Auth (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@auth/core/index.js:88:95)
[auth][error] MissingSecret: Please define a `secret`. Read more at https://errors.authjs.dev#missingsecret
    at assertConfig (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@auth/core/lib/utils/assert.js:66:16)
    at Auth (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@auth/core/index.js:88:95)
ClientPage server component
 {
  session: {
    message: 'There was a problem with the server configuration. Check the server logs for more information.'
  }
}
ClientExample client component
 {
  useSessionReturnValue: {
    data: {
      message: 'There was a problem with the server configuration. Check the server logs for more information.'
    },
    status: 'authenticated',
    update: [AsyncFunction: update]
  }
}
 GET /client-example 200 in 3548ms
 ✓ Compiled in 384ms (385 modules)
[auth][error] MissingSecret: Please define a `secret`. Read more at https://errors.authjs.dev#missingsecret
    at assertConfig (webpack-internal:///(middleware)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@auth/core/lib/utils/assert.js:66:16)
    at Auth (webpack-internal:///(middleware)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@auth/core/index.js:88:95)
[auth][error] MissingSecret: Please define a `secret`. Read more at https://errors.authjs.dev#missingsecret
    at assertConfig (webpack-internal:///(middleware)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@auth/core/lib/utils/assert.js:66:16)
    at Auth (webpack-internal:///(middleware)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@auth/core/index.js:88:95)
 ○ Compiling /auth/[...nextauth] ...
 ✓ Compiled /auth/[...nextauth] in 530ms (863 modules)
[auth][error] MissingSecret: Please define a `secret`. Read more at https://errors.authjs.dev#missingsecret
    at assertConfig (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@auth/core/lib/utils/assert.js:66:16)
    at Auth (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@auth/core/index.js:88:95)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async /Users/me/development/projects/next-auth-response-repro/node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:55759
    at async eO.execute (/Users/me/development/projects/next-auth-response-repro/node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:46527)
    at async eO.handle (/Users/me/development/projects/next-auth-response-repro/node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:57093)
    at async doRender (/Users/me/development/projects/next-auth-response-repro/node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/next/dist/server/base-server.js:1345:42)
    at async cacheEntry.responseCache.get.routeKind (/Users/me/development/projects/next-auth-response-repro/node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/next/dist/server/base-server.js:1555:40)
 GET /auth/session 500 in 1128ms
[auth][error] MissingSecret: Please define a `secret`. Read more at https://errors.authjs.dev#missingsecret
    at assertConfig (webpack-internal:///(middleware)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@auth/core/lib/utils/assert.js:66:16)
    at Auth (webpack-internal:///(middleware)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@auth/core/index.js:88:95)
[auth][error] MissingSecret: Please define a `secret`. Read more at https://errors.authjs.dev#missingsecret
    at assertConfig (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@auth/core/lib/utils/assert.js:66:16)
    at Auth (webpack-internal:///(rsc)/./node_modules/.pnpm/@[email protected]_@[email protected]/node_modules/@auth/core/index.js:88:95)
    at async /Users/me/development/projects/next-auth-response-repro/node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:55759
    at async eO.execute (/Users/me/development/projects/next-auth-response-repro/node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:46527)
    at async eO.handle (/Users/me/development/projects/next-auth-response-repro/node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:57093)
    at async doRender (/Users/me/development/projects/next-auth-response-repro/node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/next/dist/server/base-server.js:1345:42)
    at async cacheEntry.responseCache.get.routeKind (/Users/me/development/projects/next-auth-response-repro/node_modules/.pnpm/[email protected][email protected][email protected][email protected]/node_modules/next/dist/server/base-server.js:1555:40)
 GET /auth/session 500 in 15ms

Accessing http://localhost:3000/client-example generates this log on the client:

ClientExample client component
{useSessionReturnValue: {…}}
    useSessionReturnValue:
        data: {message: 'There was a problem with the server configuration. Check the server logs for more information.'}
        status: "authenticated"
        update: ƒ async update(data)
        [[Prototype]]: Object
    [[Prototype]]: Object

ClientExample client component 
{useSessionReturnValue: {…}}
    useSessionReturnValue: 
        data: {message: 'There was a problem with the server configuration. Check the server logs for more information.'}
        status: "authenticated"
        update: ƒ async update(data)
        [[Prototype]]: Object
    [[Prototype]]: Object

Image with src "/logo.png" has either width or height modified, but not the other. If you use CSS to change the size of your image, also include the styles 'width: "auto"' or 'height: "auto"' to maintain the aspect ratio.

GET http://localhost:3000/auth/session 500 (Internal Server Error)
ClientFetchError: There was a problem with the server configuration. Check the server logs for more information.. Read more at https://errors.authjs.dev#autherror

ClientExample client component
{useSessionReturnValue: {…}}
    useSessionReturnValue: 
        data: null
        status: "unauthenticated"
        update: ƒ async update(data)
        [[Prototype]]: Object
    [[Prototype]]: Object

ClientExample client component 
{useSessionReturnValue: {…}}
    useSessionReturnValue: 
        data: null
        status: "unauthenticated"
        update: ƒ async update(data)
        [[Prototype]]: Object
    [[Prototype]]: Object

GET http://localhost:3000/auth/session 500 (Internal Server Error)

ClientFetchError: There was a problem with the server configuration. Check the server logs for more information.. Read more at https://errors.authjs.dev#autherror

Note that all server-side logs and the initial couple client side logs indicate that the user is status: 'authenticated'.

Expected behavior

When AUTH_SECRET is not set:

  • useSession should return data: null or throw an error, and should not indicate status: 'authenticated'
  • NextAuth(...).auth() could potentially reject its promise / throw? Maybe the Response.json({ message }, { status: 500 }) returned by @auth/core is good enough, but that doesn't come through as an error in any way as the 500 is lost along the way back to NextAuth(...).auth()
@10hendersonm 10hendersonm added bug Something isn't working triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime. labels Sep 30, 2024
@s-h-a-d-o-w
Copy link

This isn't just a problem with useSession but also when using auth in server components, as documented here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime.
Projects
None yet
Development

No branches or pull requests

2 participants