nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
24.22k stars 3.37k forks source link

SvelteKit Auth - signinUrl using wrong protocol and Cross-site POST form submissions error #6451

Open megagames-me opened 1 year ago

megagames-me commented 1 year ago

Environment

I'm using sveltekit, so I'll give the versions relevant to it.

"devDependencies": {
    "@sveltejs/adapter-auto": "^1.0.0",
    "@sveltejs/kit": "^1.0.0",
    "svelte": "^3.54.0",
    "vite": "^4.0.0"
},
"dependencies": {
    "@auth/core": "^0.2.5",
    "@auth/sveltekit": "^0.1.12"
}

I'm using node.js 18.2.0 on replit using Ubuntu 20.04.2 LTS (Focal Fossa). My browser is chrome 108.0.5359.124.

Reproduction URL

https://replit.com/@grapecoder/amc

Describe the issue

When setting up Auth.js for SvelteKit, I followed the instructions in the documentation here. However, when trying to have my sign in button do the function signIn(), nothing was happening when I clicked on it. In the console, I got a 403 error saying that Cross-site POST form submissions are forbidden. So, after that I tried copying the method in the example repo which uses a link to /auth/signin/ instead. However, that still didn't work as when I click on the Sign in with Google button I get the same error and an alert saying that this form isn't secure. I believe the issue is due to the callbackUrl and signinUrl using the http protocol instead of https. This is my first time using this library in a while and back then there was an environment variable named NEXTAUTH_URL that would fix this issue but apparently it doesn't exist anymore. I'm using the google provider so I tried going through the code for the provider and the OAuthConfig interfaces to see whether I could modify this but I couldn't find anything.

Here is some of my code:

// /src/hooks.server.ts
import { SvelteKitAuth } from "@auth/sveltekit"
import Google from "@auth/core/providers/google"
import { GOOGLE_ID, GOOGLE_SECRET } from "$env/static/private"

export const handle = SvelteKitAuth({
    //@ts-expect-error issue https://github.com/nextauthjs/next-auth/issues/6174
    providers: [Google({
        clientId: GOOGLE_ID,
        clientSecret: GOOGLE_SECRET,
    })]
});
// /src/routes/+layout.server.ts
import type { LayoutServerLoad } from './$types';

export const load: LayoutServerLoad = async (event) => {
  return {
    session: await event.locals.getSession()
  };
}; 
<!-- /src/routes/+layout.svelte -->
<script>
    import '../app.postcss';

  import { DarkMode } from 'flowbite-svelte';
    import { Navbar, NavBrand, NavLi, NavUl, NavHamburger, Button, Input, P } from 'flowbite-svelte';
    import { page } from "$app/stores";
    import { signIn, signOut } from "@auth/sveltekit/client"
</script>

<div>
    <Navbar let:hidden let:toggle>
  <NavBrand href="/">
    <img
      src="https://flowbite.com/docs/images/logo.svg"
      class="mr-3 h-6 sm:h-9"
      alt="Flowbite Logo"
    />
    <span class="self-center whitespace-nowrap text-xl font-semibold dark:text-white">
      AMC Trainer
    </span>
  </NavBrand>
  <div class="flex md:order-2">
        <DarkMode class="mr-3" />
        {#if $page.data.session}
            <P>Signed in as {$page.data.session.user?.name}</P>
        {:else}
            <Button size="sm" href="/auth/signin" data-sveltekit-preload-data="off">Sign in</Button>    
        {/if}
    <NavHamburger on:click={toggle} />
  </div>
  <NavUl {hidden} class="order-1">
    <NavLi href="/" active={$page.url.pathname == "/"}>Home</NavLi>
    <NavLi href="/about">About</NavLi>
    <NavLi href="/services">Services</NavLi>
    <NavLi href="/pricing">Pricing</NavLi>
    <NavLi href="/contact">Contact</NavLi>
  </NavUl>
</Navbar>
<!--text-slate-900 dark:text-slate-100  -->
<div class="my-5 lg:mx-40 md:mx-20 mx-10 ">
    <slot />
</div>

</div>

How to reproduce

Follow the exact same instructions at https://authjs.dev/reference/sveltekit/modules/main but use the google provider instead.

Expected behavior

It should redirect to the google sign in page.

megagames-me commented 1 year ago

After further debugging, I believe this issue is either on my end (replit) or SvelteKit's end. In hooks.server.ts, the handle function is passed an object with the keys event and resolve. The event object has a URL object inside of it which is apparently used by Auth.js but it gives a protocol of http instead of https. Also, the request object inside of the event object has a URL object too which is wrong. Here is the modified code that corrects this. This should fix the issue.

// src/hooks.server.ts
let handlerFunc = SvelteKitAuth({
    //@ts-expect-error issue https://github.com/nextauthjs/next-auth/issues/6174
    providers: [Google({
        clientId: GOOGLE_ID,
        clientSecret: GOOGLE_SECRET,
    })],
    useSecureCookies: true,
    trustHost: true,
    debug:true,

});

export const handle = ({event, resolve}: any) => {
    event.url.protocol = "https:";

    const symbol = Object.getOwnPropertySymbols(event.request)[1];

    event.request[symbol].url.protocol = "https:";
    for (let i = 0; i < event.request[symbol].urlList.length; i++) {
        event.request[symbol].urlList[i].protocol = "https:";
    }

    return handlerFunc({event, resolve});       
};
Bewinxed commented 1 year ago

After further debugging, I believe this issue is either on my end (replit) or SvelteKit's end. In hooks.server.ts, the handle function is passed an object with the keys event and resolve. The event object has a URL object inside of it which is apparently used by Auth.js but it gives a protocol of http instead of https. Also, the request object inside of the event object has a URL object too which is wrong. Here is the modified code that corrects this. This should fix the issue.

// src/hooks.server.ts
let handlerFunc = SvelteKitAuth({
  //@ts-expect-error issue https://github.com/nextauthjs/next-auth/issues/6174
  providers: [Google({
      clientId: GOOGLE_ID,
      clientSecret: GOOGLE_SECRET,
  })],
  useSecureCookies: true,
  trustHost: true,
  debug:true,

});

export const handle = ({event, resolve}: any) => {
  event.url.protocol = "https:";

  const symbol = Object.getOwnPropertySymbols(event.request)[1];

  event.request[symbol].url.protocol = "https:";
  for (let i = 0; i < event.request[symbol].urlList.length; i++) {
      event.request[symbol].urlList[i].protocol = "https:";
  }

  return handlerFunc({event, resolve});       
};

Having the same issue here, did you figure out a fix?

I'm using discord and whenever i use the website through the domain i get 403 on the /auth/discord/signin endpoint :(

megagames-me commented 1 year ago

The code I put there was my solution. Did it not work for you? It changes the protocol to https which should fix all your issues.

Please give me further information if my solution didn't work for you.

ollema commented 1 year ago

@megagames-me

The code works, but should this really be needed?

It feels very odd to me!

megagames-me commented 1 year ago

@megagames-me

The code works, but should this really be needed?

It feels very odd to me!

I'm not sure why, but the library thinks that the website is on http not https. I looked through the code of Auth.js and found that this variable is what is used to check whether it should redirect to http or https. I'm not sure whether the UrlList thing is necessary though. It is quite odd.

I probably shouldn't have closed this issue since it's just a temporary fix anyway. I'll reopen it now.

WolfVector commented 1 year ago

I encountered the same issue using sveltekit, but only when I tried production mode. The reason of the issue is that auth.js tries to run on https when in production and if you don't configure your sveltekit app to detect the https protocol or to ignore it, then you are going to face this problem.

I fixed by modifying the .env file. In the deployment page https://kit.svelte.dev/docs/adapter-node you have the option of adding some variables, one of them is your origin url:

ORIGIN=https://my.site

# or e.g. for local previewing and testing
ORIGIN=http://localhost:3000

In my case I added ORIGIN=http://localhost:3000 because I just want to test the deployment.

konstantinblaesi commented 7 months ago

very annoying/confusing bug. I am running with https in development mode using vite-plugin-mkcert calling https://localhost:5173/auth/session after a successful login would show my session details but my sveltekit layouts/pages calling event.locals.auth() or event.locals.getSession() would always receive null

when debugging I also noticed that the difference between the two is that authjs thinks I am on http in the failing case and fails because it is looking for thet http session token cookie (names differ)

my fix is

SvelteKitAuth({
  useSecureCookies: true
})
megagames-me commented 7 months ago

very annoying/confusing bug. I am running with https in development mode using vite-plugin-mkcert calling https://localhost:5173/auth/session after a successful login would show my session details but my sveltekit layouts/pages calling event.locals.auth() or event.locals.getSession() would always receive null

when debugging I also noticed that the difference between the two is that authjs thinks I am on http in the failing case and fails because it is looking for thet http session token cookie (names differ)

my fix is

SvelteKitAuth({
  useSecureCookies: true
})

I think maybe create a new issue? The problem I was talking about seems to be fixed in that context but you seem to be having it in a different one. Maybe a new issue would bring more attention to it