Next-auth: Problem with serverside getSession

Created on 26 Jun 2020  Â·  19Comments  Â·  Source: nextauthjs/next-auth

Please wait before you close this issue.

As mentioned before I've double checked my env variables for SITE env.

  1. On dev env I set it as http://localhost:3000 - doesnt work
  2. I deployed it to vercel and set env to proper url - doesnt work

I even tried using NEXT_PUBLIC_SITE env but nothing seems to be working...

Btw. Im sure I set up my env var properly, to make sure I made component to display it on screen and it shows full valid url

Sorry for spamming

bug

Most helpful comment

@nemmtor Hello! I'm sorry, I think I owe you an apology.

After I repeatedly and strenuously denied this was a bug (like a lot of other erroneous reports before it), I think I found what was actually triggering the issue (which is very embarrassing).

Depending on how the site is bundled / deployed, if a server side route is called before _app.js is ever invoked (e.g. before any other page is rendered by the server / lambda function) then the site name global ends up not being set when a call to getSession() is made. This likely triggered the error you saw.

It is probably more visible on localhost in development when reloading the same page over and over (as in practice, going to a production site meant at least one other page on the site had been hit first by another user, so it wouldn't likely be triggered).

While working on some changes for v3 I ended up refactoring configuration works for the site name completely to prevent this and reduce complexity across all pages/API routes. Instead of the site option, users are now asked to set the NEXTAUTH_URL environment variable which defines the site URL in a robust way across the app.

On localhost it is set automatically and if using Vercel NextAuth.js will use VERCEL_URL (which is set automatically by Vercel) but NEXTAUTH_URL should be set for everything to work correctly as VERCEL_URL is the instance name rather than the actual name for the site people might expect it to see.

I've posted a change log for the beta with a link to the live demo at https://github.com/iaincollins/next-auth/issues/384

I'll credit you accordingly in the release notes. Sorry again for dismissing the bug report!

All 19 comments

I can't replicate this and I don't think it's a bug in NextAuth.js, sorry.

I would try cloning the example repo and seeing if you can get that working and then compare the differences.

Just been thinking about this…

You said "_I'm sure I set up my env var properly, to make sure I made component to display it on screen and it shows full valid url_" and that made me wonder where you are defining the environment variables.

That is curious as environment variables are not exported to the client by default, so I would not expect it to show anything for an environment variable on a page - it will be available on the server side render pass, but not on the client side render pass when the page hydrates, so will instantly "disappear" unless viewed in a browser without JavaScript enabled.

Note that environment variables needed server side cannot be defined in the env option of next.config.js, that is only for exporting variables client side

If you define something there there it will not be available server side, which is why it would be empty in _app.js (which is run both server side and client).

If you want to export an environment variable it needs to be in an .env file (as in the example repo) or defined with the deployment.

e.g. using next env add or under "Project Settings" on Vercel.com.

Well i've added console log inside getServerSideProps to make sure my env var is defined on server side:
image
And it works, it logs http://localhost:3000

Funny thing, the problem doesnt exist anymore on dev, it just started working without any reason

But sadly it doesnt work in production

I made another check to make sure my vercel env is set up properly, thats what I did:

export async function getServerSideProps({ req, res }) {
  return { props: { site: process.env.SITE } };
}

then in page itself:


export default function Dashboard({ site }) {
  return (
    <>
      <pre>{JSON.stringify(site, null, 2)}</pre>
      <button type="button" onClick={signout}>
        Sign out!
      </button>
    </>
  );
}

Result:

image

So as you can see I can access process.env.SITE on server side. Maybe quotation marks around url is what is the problem here?

I think it's worth checking out the example repo. It has an example.env file which shows how to set things up. I think quotes would cause a problem.

I think it's best to start with that example before adding it to a new site, just to try things out and get familiar with the configuration. NextAuth.js (and Next.js) tries to make things easy, but there is a lot of inherent complexity in some aspects (like public/private or client/sever env vars).

Okay so after many tries i've discovered that server side redirection is causing this error:

if (!session) {
    ctx.res.writeHead(302, {Location:'/'});
    ctx.res.end();
    return {
      props: {}
    };
  }

After removing redirection and refactoring a bit I am able to access the page without error.

But now I cannot do server side redirection. Any solution for that? Is it next-auth bug then?

export async function getServerSideProps(ctx) {
  const session = await getSession(ctx);
  if (!session) {
    return {
      props: {
        user: {
          error: 'iserror'
        }
      }
    };
  }
  const { user } = session;
  return {
    props: { user },
  };
}

What is the error you get now?

Did you try the example project?

Yes I tried everything is working fine but I got error about absolute URLs after adding server side redirection

You still have the same problem with your configuration.

I just tried this in the example project, see below.

import { getSession } from 'next-auth/client'
import Layout from '../components/layout'

export default ({user}) => (
  <Layout>
    <h1>Server Side Rendering</h1>
    <p>{JSON.stringify(user, null, 2)}</p>
  </Layout>
)

export async function getServerSideProps(ctx) {
  const session = await getSession(ctx)
  if (!session) {
    return {
      props: {
        user: {
          error: 'iserror'
        }
      }
    }
  }
  const { user } = session;
  return {
    props: { user },
  }
}

Screenshot 2020-06-26 at 23 14 39
Screenshot 2020-06-26 at 23 14 49

This is not a bug in NextAuth.js.

Does it work if you try adding ctx.res.writeHead(302, {Location: '/'});
ctx.res.end()

Inside if(!session)?

This is what is causing absolute URL error inside my code

No I get this error, which is completely different.

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

This is an issue doing redirects in Next.js.

There are some discussions of it here:

https://github.com/vercel/next.js/discussions/11072
https://github.com/vercel/next.js/discussions/11281

It looks like some people have suggestions on there and a Pull Request.

Until the team has an update that resolves the issue, you can do something like this:

import { getSession } from 'next-auth/client'
import Layout from '../components/layout'

const REDIRECT_URL = '/'

const Page = ({session}) => {
  if (!session && typeof window !== 'undefined') {
    window.location = REDIRECT_URL
    return null
  }
  return (
    <Layout>
      <h1>Server Side Rendering</h1>
      <p>{JSON.stringify(session, null, 2)}</p>
    </Layout>
  )
}

Page.getInitialProps = async ({req, res}) => {
  const session = await getSession({req})

  if (!session && req) {
    res.writeHead(302, { Location: REDIRECT_URL }).end()
    return {}
  }

  return { session }
}

export default Page

This ensures that in both client and server side flows it will redirect and not render. Having the location redirect in the render() is messy but is required. If try doing it from the getInitialProps() it will not work properly.

This is not a NextAuth.js specific approach or work around, rather it is a Next.js issue.

I might add a page to the example project which has logic like this to help people implement it more easily if it seems like a lot of folks are curious about how to do it.

Because of how tricky redirects are in Next.js, usually it's just easier to have a conditional render function that displays an "Access Denied" component in the UI, and for it to contain a link to the sign in page, rather than try to have Universal logic for redirects.

e.g.

import { getSession } from 'next-auth/client'

// This can be a regular component. You can use methods like `componentDidMount()`
// to trigger client side redirect as soon as the component is loaded client side.
const AccessDenied = () =>
  <>
    <h1>Access Denied</h1>
    <p>
      <a href="/api/auth/signin">You must be signed in to view this page</a>
    </p>
  </>

export default ({session}) => {
  if (!session) { return <AccessDenied/> }

  return (
    <Layout>
      <h1>Server Side Rendering</h1>
      <p>{JSON.stringify(session, null, 2)}</p>
    </Layout>
  )
}

export async function getServerSideProps(context) {
  return {
    props: {
      session: await getSession(context)
    }
  }
}

Thank you this workaround works for now. I think we can close issue. :)

@nemmtor Hello! I'm sorry, I think I owe you an apology.

After I repeatedly and strenuously denied this was a bug (like a lot of other erroneous reports before it), I think I found what was actually triggering the issue (which is very embarrassing).

Depending on how the site is bundled / deployed, if a server side route is called before _app.js is ever invoked (e.g. before any other page is rendered by the server / lambda function) then the site name global ends up not being set when a call to getSession() is made. This likely triggered the error you saw.

It is probably more visible on localhost in development when reloading the same page over and over (as in practice, going to a production site meant at least one other page on the site had been hit first by another user, so it wouldn't likely be triggered).

While working on some changes for v3 I ended up refactoring configuration works for the site name completely to prevent this and reduce complexity across all pages/API routes. Instead of the site option, users are now asked to set the NEXTAUTH_URL environment variable which defines the site URL in a robust way across the app.

On localhost it is set automatically and if using Vercel NextAuth.js will use VERCEL_URL (which is set automatically by Vercel) but NEXTAUTH_URL should be set for everything to work correctly as VERCEL_URL is the instance name rather than the actual name for the site people might expect it to see.

I've posted a change log for the beta with a link to the live demo at https://github.com/iaincollins/next-auth/issues/384

I'll credit you accordingly in the release notes. Sorry again for dismissing the bug report!

@iaincollins that's fine. Im glad that somehow I could help finding out that bug. Happy to hear it's going to be fixed. Long time I've tried to find best solution for handling Auth in next js and your solution seems to be the best so far. It works as expected, it's pretty simple and doesn't need tons of boilerplate!

@nemmtor Hello! I'm sorry, I think I owe you an apology.

After I repeatedly and strenuously denied this was a bug (like a lot of other erroneous reports before it), I think I found what was actually triggering the issue (which is very embarrassing).

Depending on how the site is bundled / deployed, if a server side route is called before _app.js is ever invoked (e.g. before any other page is rendered by the server / lambda function) then the site name global ends up not being set when a call to getSession() is made. This likely triggered the error you saw.

It is probably more visible on localhost in development when reloading the same page over and over (as in practice, going to a production site meant at least one other page on the site had been hit first by another user, so it wouldn't likely be triggered).

While working on some changes for v3 I ended up refactoring configuration works for the site name completely to prevent this and reduce complexity across all pages/API routes. Instead of the site option, users are now asked to set the NEXTAUTH_URL environment variable which defines the site URL in a robust way across the app.

On localhost it is set automatically and if using Vercel NextAuth.js will use VERCEL_URL (which is set automatically by Vercel) but NEXTAUTH_URL should be set for everything to work correctly as VERCEL_URL is the instance name rather than the actual name for the site people might expect it to see.

I've posted a change log for the beta with a link to the live demo at #384

I'll credit you accordingly in the release notes. Sorry again for dismissing the bug report!

Is it possible to override the vercel url when deploying through vercel ? because when using apple signing, I have to provide a url for testing and I cannot use the vercel url.. since vercel url is different each time an update is pushed...

Is it possible to override the vercel url when deploying through vercel ? because when using apple signing, I have to provide a url for testing and I cannot use the vercel url.. since vercel url is different each time an update is pushed...

No, it's not possible to override VERCEL_URL. This is why we read it as a fallback in v3, but ask people to explicitly set NEXTAUTH_URL for the application (and use NEXTAUTH_URL if specified).

I've had a chat with some folks from Vercel about this and it sounds like it might be that they will add a option to set the canonical site URL in the Dashboard in future - if that happens we would support doing that so that people didn't have to configure the NEXTAUTH_URL option if hosting on Vercel.

I don't really like asking people to have to set an environment variable as it's not an ideal developer experience, so it would be great if Vercel gets something like that in future.

Is it possible to override the vercel url when deploying through vercel ? because when using apple signing, I have to provide a url for testing and I cannot use the vercel url.. since vercel url is different each time an update is pushed...

No, it's not possible to override VERCEL_URL. This is why we read it as a fallback in v3, but ask people to explicitly set NEXTAUTH_URL for the application (and use NEXTAUTH_URL if specified).

I've had a chat with some folks from Vercel about this and it sounds like it might be that they will add a option to set the canonical site URL in the Dashboard in future - if that happens we would support doing that so that people didn't have to configure the NEXTAUTH_URL option if hosting on Vercel.

I don't really like asking people to have to set an environment variable as it's not an ideal developer experience, so it would be great if Vercel gets something like that in future.

Ah yes I mean overtiding the nextauth_url. I know it’s not possible to override the vercel_url. It would be ideal if vercel provided the canonical url. Thanks for the clarification! 😀

Ah yes I mean overtiding the nextauth_url. I know it’s not possible to override the vercel_url. It would be ideal if vercel provided the canonical url. Thanks for the clarification! 😀

In v3 can set the NEXTAUTH_URL value to any hostname or URL for your site you like.

It is quite forgiving about what value is passed to it, for example any of these should work in v3:

NEXTAUTH_URL=example.com
NEXTAUTH_URL=https://example.com
NEXTAUTH_URL=https://example.com:3000/

When testing the Apple provider I use something like this:

NEXTAUTH_URL=https://test.example.com:3000/

... and configure my host file so that test.example.com points to 127.0.0.1.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

alex-cory picture alex-cory  Â·  3Comments

ryanbahan picture ryanbahan  Â·  3Comments

ghoshnirmalya picture ghoshnirmalya  Â·  3Comments

readywater picture readywater  Â·  3Comments

Xetera picture Xetera  Â·  3Comments