Closed ghoshnirmalya closed 4 years ago
Great question!
There are multiple contributing reasons as to why this needs to be set - and why it is an environment variable in v3.
API routes and Server Side Rendered functions (e.g. getServerSideProps
and getInitialProps
) need to know the site URL as they make a request back to the server - passing though the session token in the request object - to determine if a user is signed in.
This allows API routes and Server Side Rendered Pages to be lightweight and not have to pull in all the database code and other logic, which stays in the authentication route. Of course, using JSON Web Tokens sessions and the getToken
helper is even faster as you can just verify the token without having to make a network call.
In v2 the site name and base path were options on NextAuth.js configuration, and this was passed to API routes and Server Side Pages using a Secure server side only configuration cookie. This has some security challenges, even a secure, server only cookie and was problematic in practice.
It is especially problematic in development mode or when rendering Server Side Pages. On first hit of a new deploy (a particular problem on local development instances) the server rendered page would not have had access to the cookie yet and so wouldn't know how to contact the server. Even requesting the page was enough for it to see and grab the value from the cookie for later reuse (as even Serverless functions are memory resident, e.g. for ~2 hours on AWS Lambda) but it was still an edge case there was no other obvious solution for.
It's possible to grab the host name from the req
object, but this isn't a great solution as it has security problems; anyone can spoof the host
header and exploit that (e.g. in a DDoS attack against another host).
Another consideration is that the hostnames in callback URLs for most OAuth providers need to exactly match the specified domains with the OAuth provider. In the vast majority of cases you can't use a wildcard with an OAuth provider either; you have to explicitly configure each Full Qualified Domain Name (e.g. example.com
, www.example.com
) so attempting to auto-discover it it doesn't work for a lot of folks in practice (security issues aside), as it depends on which URL the user has gone to.
So, it's an environment variable, which has the added benefit of being available to all pages and routes in an application, so works great for other API routes and for Server Side Pages. The environment variable also lets you specify a custom base path on it too, so it takes care of that problem as well.
In the case of Vercel, it actually reads in the instance name of VERCEL_URL
as a fallback if NEXTAUTH_URL
is not set (and, as that has no protocol, assumes HTTPS, which it always is on Vercel). If neither environment variable is set it assumes http://localhost:3000
for the hostname. If no base path for the auth routes is specified, it uses /api/auth
as the default. This makes it really easy for folks to get started and try things out.
The VERCEL_URL
property is an instance name though, and usually not what people are expecting to use as the URL in callbacks though, so it's of limited use right now - great for Email Provider or Credentials Provider (it's a valid hostname for the site to callback to), but doesn't work for OAuth as if you are using an OAuth provider (Google, Facebook, Twitter, etc) in almost all cases you will still need to explicitly allow the hostname of the test instance to be allowed. If you are using something like your own Open ID service then you could configure it to allow subdomains, but that's a bit of an exception.
I've had a chat with the Vercel folks about this and how it would be really nice if a user could define a 'default domain' for a site and use that for VERCEL_URL
(or another, similar variable). That way it wouldn't need to be specified for production instances at least. They seem quite interested in this idea as it also address an issue they have, but I don't know if/when that would be.
However, even with that, there is still the issue for OAuth providers that they mostly (overwhelmingly) don't let you specify wildcard domains for valid callback URLs.
For test instances on Vercel, one option to get round this is to enable an Email provider or a Credentials Provider with hard coded credentials, only on Pull Request environments (e.g. if NEXTAUTH_URL
isn't set) so they can still sign in, even if it's not the typical auth flow.
@iaincollins Thank you for your detailed and quick reply.
I've removed NEXTAUTH_URL
from my list of environment variables and used VERCEL_URL
which is populated by the System variables in Vercel. However, when click on the "Sign In" button on this page, the following happens:
Try signing with a different account.
I'm not getting any error related to my code. However, the logs are present in the screenshot above.
Any idea if this could happen because of the environment variables?
Hmm, I'm not sure what's causing this - this should work!
Just to confirm the the second URL (after submitting an email address), e.g. https://nextjs-hasura-boilerplate-luzxmiugl.vercel.app, is the VERCEL_URL
. It looks odd as it's always the auto-generated instance name, even if you have a domain configured.
The URL in the address bar changes when you submit because the form always uses the canonical callback URL for a provider (including email) and is always taken from the server config to ensure it's "correct"; and in this case because there is no NEXTAUTH_URL
, is the VERCEL_URL
. While it looks odd, that is all fine and should work.
I had a go at signing in myself saw the same behaviour, the email works fine but for some reason it can't sign me in!
That's really unusual.
That's the sort of thing I would expect to see if there is a problem with the database, but the email provider uses a database so I don't know why this his happening. I assume you have upgraded to 3.0.1
already, given you were the one who spotted the bug with that. :-)
Can I ask what sort of database you are using so I can see if I can replicate the issue?
PS: The example site uses DATABASE_URL=sqlite://localhost/:memory:?synchronize=true
for an in memory SQLite database. While that's not suitable for production (only works if you have 1 x Lambda instance and Lambda instances are reset ~2 hours or more often) it's quite handy for test instances.
I'm using Postgres on Heroku (free Hobby tier).
@iaincollins I figured out the issue that was happening in my case. It was because I was signing my jwt with a secret. This thread/comment helped me with it.
@iaincollins Also, I'm doing the following:
NEXTAUTH_URL
for production deployments so that the providers callback redirects me to my canonical url.VERCEL_URL
for preview deployments so that I get redirected to the deployment url after logging in.I figured out the issue that was happening in my case. It was because I was signing my jwt with a secret. This thread/comment helped me with it.
Thanks for the update! That makes sense and is good to know.
We do the same thing for the private key on the Apple provider as how newlines get transformed in ENV VARs is pesky.
Based on your experience, happy to take any recommendations about error handling (either on the console or in API responses) that would make debugging this easier. I know there are potential areas for improvement there.
@iaincollins thanks for the thorough explanation. I think you should include this in the documentation:
In the case of Vercel, it actually reads in the instance name of VERCEL_URL as a fallback if NEXTAUTH_URL is not set (and, as that has no protocol, assumes HTTPS, which it always is on Vercel). If neither environment variable is set it assumes http://localhost:3000 for the hostname. If no base path for the auth routes is specified, it uses /api/auth as the default. This makes it really easy for folks to get started and try things out.
Thanks!
@ghoshnirmalya @iaincollins
Best I can tell VERCEL_URL
is dynamic, and thus cannot be leveraged with most OAuth providers (like Google, in my case). What am I missing here?
When I follow this:
@iaincollins Also, I'm doing the following:
- Setting
NEXTAUTH_URL
for production deployments so that the providers callback redirects me to my canonical url.- Setting
VERCEL_URL
for preview deployments so that I get redirected to the deployment url after logging in.
I get VERCEL_URL
s that look like this:
foo.username.vercel.app
foo-git-staging.username.vercel.app
foo-5c5m1a3ef.vercel.app
foo-c135l33jd.vercel.app
foo-p2fxqw7m1.vercel.app
foo-o1s7js5is.vercel.app
...
My goal is to be able to test Auth on the previews, but that doesn't appear feasible at the moment?
@iaincollins Where should one set NEXTAUTH_URL if not deploying to vercel? I use serverless next.js component (i.e. deploying to AWS Lambda@Edge & Cloudfront). Is it enough to set NEXTAUTH_URL in env.local file? Best Regards
+1 for this issue. We are trying to set up oauth with Microsoft azure, and this requires to set up a safelist with all the known hosts of our app. Our solution is to create multiple environments as branches and use the fixed names of the vercel branch deployments as callback URLs (set up in azure for the oauth app).
However, since VERCEL_URL
injects the instance URLs instead of the fixed names, the redirect goes back to foo-5c5m1a3ef.vercel.app
instead of foo-git-staging.username.vercel.app
(which is safelisted in azure).
Any idea on how to solve that?
For anyone else looking for workarounds...
If you need different NEXTAUTH_URL
s for different branch deployments like I did, you can use next-branch-env to expose the env vars
NEXTAUTH_URL=https://myapp.com # production branch
STAGING_NEXTAUTH_URL=https://staging.myapp.com # preview branch
DEV_NEXT_NEXTAUTH_URL=https://dev.myapp.com # preview branch
as NEXTAUTH_URL
on their respective branches. This happens at build time, rather than at runtime, so correct me if I'm wrong, but I don't see any new security concerns. VERCEL_URL
did not work for us, because it does not expose our custom domain used for deployments.
Alternatively, you can set a global "root" site host (in Vercel Environment Variables):
ROOT_HOST=myapp.com
and programmatically (in your next.config.js
file) define NEXTAUTH_URL
based on the host and branch:
// next.config.js
const withBranchEnv = require('next-branch-env')({ expose: 'BRANCH' })
process.env.NEXTAUTH_URL = `https://${process.env.BRANCH}.${process.env.ROOT_HOST}`
module.exports = withBranchEnv(/* config */)
Thanks @lukeburns, that's really interesting and I'm sure helpful for folks!
@breytex Sorry we don't have a better answer yet, but is on the radar.
@PaulKushch
@iaincollins Where should one set NEXTAUTH_URL if not deploying to vercel? I use serverless next.js component (i.e. deploying to AWS Lambda@Edge & Cloudfront). Is it enough to set NEXTAUTH_URL in env.local file?
Sorry this reply is late, but for anyone else coming to this, if using AWS Lambda, then in the Serverless config is fine (if using Serverless) or in the config page for the Lambda on AWS console (you should be able to set it once and have it persist).
You can also set env vars manually via Vercel/Heroku/AWS dashboard.
NB: AWS Lambda@Edge (as opposed to AWS Lambda) is a tricky platform to support. Folks have been using it successfully (and am happy to take PRs related to supporting it!) but it's not something we test against so you might bump into issues with it.
Using NextAuth.js with CloudFront is fine. Running inside a CloudFront Workers is currently supported (as they are not Node.js), but running behind a CloudFront Worker (e.g. which checks a JWT set by NextAuth.js to do access control) is a great way to control access to content.
I do this with my team by having them do preview deployments like this.
vc -e NEXTAUTH_URL=https://user-preview-url.vercel.app
Unfortunately it won't scale with a large team, but at least it works for now, but so far we've only needed this for social login and some webhook apis.
Which allows you to set the env vars individually per dev.
For anyone trying to get nextauth working on vercel preview environments I got it working by following the secure cookie issue made by @cathykc above https://github.com/nextauthjs/next-auth/pull/1672#issuecomment-843697429
Basically just:
NEXTAUTH_URL
env variable from previewssecureCookie
option in getToken
returns true for preview urlsIt would be really helpful if url
was a parameter that could be passed in to NextAuthOptions
. That way we could set it to whatever we wanted.
How does setting a different/dynamic NEXTAUTH_URL
help if GitHub fails any callback redirects if their destination differs from the "Callback URL" in the OAuth app's settings?
Can somebody please post a full working example of Next Auth support in Vercel preview deployments?
a summary on authentication in preview environments:
Some questions regarding Netlify preview deployments: https://github.com/nextauthjs/next-auth/discussions/3176
I am not clear if NEXTAUTH_URL
should be set in build time for production or I can leave it undefined on build and set it just in runtime? Docs is not clear about this and it's very important.
On most platforms, you should ALWAYS set NEXTAUTH_URL
. On Vercel, we try to detect the domain, which seem to be currently broken, and is tracked here: https://github.com/nextauthjs/next-auth/issues/3419
Is next-auth reading that environment variable during build time or run time? We strive to have all our systems obey the 12-factor app rules (https://12factor.net/) and one of the big ones is being able to build once and run in multiple different configurations/environments.
Is next-auth compatible with that approach?
Was this fixed since that, or is this still using build time env vars?
Another issue is VERCEL_URL
does not match the actual URL on previous deployments. Sometimes it fits sometimes it doesn't. And for this, I can't make a decent test environment for NextAuth.js
Is next-auth reading that environment variable during build time or run time? We strive to have all our systems obey the 12-factor app rules (https://12factor.net/) and one of the big ones is being able to build once and run in multiple different configurations/environments.
Is next-auth compatible with that approach?
It reads variables at build time not run time. If you manually add the correct URL to env
it'll not affect until you rebuild it.
not sure why this is closed but if there is a real solution about this issue should be better explained on the docs https://next-auth.js.org/getting-started/example
Then on deployment page https://next-auth.js.org/deployment seems is saying exactly the opposite.
a summary on authentication in preview environments:
@balazsorban44 - I would like to give it a read but this link is broken, can you please share the updated one? Thanks!
@balazsorban44 Any update on the link?
My goal is to be able to test Auth on the previews, but that doesn't appear feasible at the moment?
@DeBraid - I am in similar situation now, were you able to solve this problem?
However, since
VERCEL_URL
injects the instance URLs instead of the fixed names, the redirect goes back tofoo-5c5m1a3ef.vercel.app
instead offoo-git-staging.username.vercel.app
(which is safelisted in azure).Any idea on how to solve that?
@breytex - I am in similar situation now, were you able to solve this problem?
For test instances on Vercel, one option to get round this is to enable an Email provider or a Credentials Provider with hard coded credentials, only on Pull Request environments (e.g. if NEXTAUTH_URL isn't set) so they can still sign in, even if it's not the typical auth flow.
@iaincollins Can you elaborate on this approach? How can we fake auth for a signed in user tied to a real record in the preview app DB?
One idea: NextAuth can encode the initial URL (preview URL) in the OAuth state and do another redirect after auth is successful with the production instance (after coming back from the provider to the production instance) to do another redirect and set-cookie for the initial URL and get user back where they started.
getServerSession returns null for some reason, I'm using Next pages with trpc, and calling getServerSession
inside trpc's createContext
For people from SvelteKit eco-system, I have written a blog on how we can achieve the same using fake identity server. Do give it a read!
I'm using Next-Auth and this solved for me:
NEXTAUTH_URL
to development environment..env
Code for lazy people:
echo "http://localhost:3000" | vercel env add NEXTAUTH_URL development
vercel env pull
vercel build
Vercel has the option to preview deployments of your pull requests before merging them. As a result, setting
NEXTAUTH_URL
would be tricky as the deployment url of a preview pull request might be different from the production url.Your question Is it possible to set the value of
NEXTAUTH_URL
dynamically? Also, why is this environment variable necessary to be set?