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

trailingSlash does not work in next-auth@v5 #10127

Closed
ablackoff opened this issue Feb 25, 2024 · 6 comments · Fixed by #10752
Closed

trailingSlash does not work in next-auth@v5 #10127

ablackoff opened this issue Feb 25, 2024 · 6 comments · Fixed by #10752
Labels
bug Something isn't working triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime.

Comments

@ablackoff
Copy link

Environment

System:
OS: macOS 14.4
CPU: (10) arm64 Apple M1 Max
Memory: 256.14 MB / 32.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 20.8.0 - /usr/local/bin/node
Yarn: 1.22.19 - /usr/local/bin/yarn
npm: 10.2.5 - /usr/local/bin/npm
pnpm: 8.15.4 - /usr/local/bin/pnpm
bun: 1.0.0 - ~/.bun/bin/bun
Browsers:
Chrome: 122.0.6261.69
Safari: 17.4
npmPackages:
next: latest => 14.1.0
next-auth: beta => 5.0.0-beta.13
react: ^18.2.0 => 18.2.0

Reproduction URL

https://github.com/ablackoff/next-auth-example

Describe the issue

When adding trailingSlash: true to the next.config.js file, the following error occurs:
In the browser:

"Error: This action with HTTP POST is not supported."

In the console:

[auth][error] UnknownAction: Cannot parse action at /api/auth/signin/github/ .Read more at https://errors.authjs.dev#unknownaction
    at parseActionAndProviderId (webpack-internal:///(rsc)/./node_modules/@auth/core/lib/utils/web.js:90:49)
    at toInternalRequest (webpack-internal:///(rsc)/./node_modules/@auth/core/lib/utils/web.js:32:40)
    at Auth (webpack-internal:///(rsc)/./node_modules/@auth/core/index.js:82:103)
    at httpHandler (webpack-internal:///(rsc)/./node_modules/next-auth/index.js:139:80)
    at /Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:63815
    at /Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/server/lib/trace/tracer.js:133:36
    at NoopContextManager.with (/Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/compiled/@opentelemetry/api/index.js:1:7062)
    at ContextAPI.with (/Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/compiled/@opentelemetry/api/index.js:1:518)
    at NoopTracer.startActiveSpan (/Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/compiled/@opentelemetry/api/index.js:1:18093)
    at ProxyTracer.startActiveSpan (/Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/compiled/@opentelemetry/api/index.js:1:18854)
    at /Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/server/lib/trace/tracer.js:122:103
    at NoopContextManager.with (/Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/compiled/@opentelemetry/api/index.js:1:7062)
    at ContextAPI.with (/Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/compiled/@opentelemetry/api/index.js:1:518)
    at NextTracerImpl.trace (/Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/server/lib/trace/tracer.js:122:28)
    at /Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:56866
    at AsyncLocalStorage.run (node:async_hooks:346:14)
    at Object.wrap (/Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:37102)
    at /Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:54639
    at AsyncLocalStorage.run (node:async_hooks:346:14)
    at Object.wrap (/Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:36351)
    at /Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:54601
    at AsyncLocalStorage.run (node:async_hooks:346:14)
    at eU.execute (/Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:53994)
    at eU.handle (/Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/compiled/next-server/app-route.runtime.dev.js:6:65073)
    at doRender (/Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/server/base-server.js:1333:60)
    at cacheEntry.responseCache.get.routeKind (/Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/server/base-server.js:1543:46)
    at ResponseCache.get (/Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/server/response-cache/index.js:49:26)
    at DevServer.renderToResponseWithComponentsImpl (/Users/a.blackoff/Projects/my/next-auth-example-v2/node_modules/next/dist/server/base-server.js:1463:53)

Exactly the same behavior with the Credentials provider.

This problem started with version "next-auth": "5.0.0-beta.6" and higher

How to reproduce

Go to the "Sign in" button, then "Sign in with GitHub", then you will immediately go to the error

Expected behavior

Must go to Github authorization. Don't get the error "Error: This action with HTTP POST is not supported."

@ablackoff ablackoff added bug Something isn't working triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime. labels Feb 25, 2024
@stefanullinger
Copy link

I was having the same issue. Needed to turn off trailingSlash for now.

@ablackoff
Copy link
Author

@stefanullinger Yes, I realized that I need to disable it for it to work.
I'm trying to highlight here that this feature would be good to fix

@lurepheonix
Copy link

lurepheonix commented Mar 25, 2024

The error in question was introduced in commit fc358df.

So, what we've got here:

export function parseActionAndProviderId(
  pathname: string,
  base: string
): {
  action: AuthAction
  providerId?: string
} {
  const a = pathname.match(new RegExp(`^${base}(.+)`))

  if (a === null)
    throw new UnknownAction(`Cannot parse action at ${pathname}`)

  const [_, actionAndProviderId] = a

  const b = actionAndProviderId.replace(/^\//, "").split("/")

  if (b.length !== 1 && b.length !== 2)
    throw new UnknownAction(`Cannot parse action at ${pathname}`)

  ...etc
}

The actual error happens at if (b.length !== 1 && b.length !== 2).
When we are using trailing slashes, our pathname argument becomes /api/auth/callback/credentials/ (in case of using Credentials provider).
In this case, a will contain ["/api/auth/callback/credentials/","/callback/credentials/"].
So:

  • actionAndProviderId.replace(/^\//, "") will be callback/credentials/
  • b will be ["callback","credentials",""] because callback/credentials/ is split by / and thus b.length will be 3, thus leading to failure.

Three of the easiest ways to solve:

  1. Remove empty strings from b array with Array.filter: const b = actionAndProviderId.replace(/^\//, "").split("/").filter((el) => el !== '');. Or even replace the first and the last slash occasions: const b = actionAndProviderId.split("/").filter((el) => el !== ''); (tested, fixes the bug, suggest going with it)
  2. Add the trailing slash to the regex that generates a: new RegExp(`^${base}(.+)/`) (tested, fixes the bug, will fail on something like /api/auth/callback/credentials//// — but do we actually need that usecase?)
  3. Trim the last slashes manually.

Or use some other approach.

@chrislambe @balazsorban44 it's your call :) I can save you some time by creating a PR with changes once we settle on the approach.

@chrislambe
Copy link
Contributor

I think you'll find that this behavior was not introduced in fc358df since the previous implementation:

const [_, actionAndProviderId] = "/api/auth/callback/credentials/".split("/api/auth")
const b = actionAndProviderId.replace(/^\//, "").split("/")

...and the current implementation:

const [_, actionAndProviderId] = "/api/auth/callback/credentials/".match(new RegExp(`^/api/auth(.+)`))
const b = actionAndProviderId.replace(/^\//, "").split("/")

...produce the same value for b.

But since I'm here, I'll cast my vote for the third suggested option:

const b = actionAndProviderId.replace(/\/$/, '').replace(/^\//, '').split("/")

...or, if a case like /api/auth/callback/credentials//// is supported for some reason:

const b = actionAndProviderId.replace(/\/+$/, '').replace(/^\//, '').split("/")

Might be able to take both slashes out in a single method call but it might be better to just keep it simple.

@thosil
Copy link

thosil commented Apr 25, 2024

sorry to ask, any news on this one?
I just tried 5.0.0-beta.16 and the issue is still present.
Unfortunately I don't have the time to make a PR.

@ndom91
Copy link
Member

ndom91 commented Apr 28, 2024

@lurepheonix @chrislambe thanks for diving into this and suggesting some solutions!

I've opened a PR basically following suggestion 1, by adding the .filter(Boolean) to get rid of the empty parsed array members.

Let me know if that works for yall 👍

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

Successfully merging a pull request may close this issue.

6 participants