nextauthjs / next-auth

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

Add support for JSON Web Tokens (JWT) #167

Closed iaincollins closed 4 years ago

iaincollins commented 4 years ago

I'm keen to add JWT support, so that sessions don't need to be stored in a database.

This has really interesting implications; if you don't need a session database, it's possible to have an authenticated service that doesn't require a database. This is very quick to set up and very well suited to Single Page Apps in React and to Serverless.

Examples of how you could create a service that supports login but doesn't require a database:

I think we could do this without a major refactor, and am even tempted to make it the default behaviour if you don't specify a database (with perhaps options to specify callback functions for account creation and fetching sessions, so you can easily customise what should be in your JWT), as it would make it very easy for people to setup authentication and worry about details later.

iaincollins commented 4 years ago

See also #153

positonic commented 4 years ago

I might be able to start on this immediately if it aligns with my requirements.

I have an existing GraphQL server which has a register and login "endpoint", that returns a user object and a token.

I like the look of next-auth and would like to use it to site between my backend and my NextJS app.

Does this sounds like a match with this requirement @iaincollins ?

If so I have more questions.

iaincollins commented 4 years ago

So, adding this is actually super simple for me to do and I think there is a good chance I will add it over the weekend - I've been working on documentation this week and that's been helpful to think about the API and how the options fit in.

I am actually thinking of making JWT tokens the default behaviour if no database is provided, so signing in 'just works' without a database, as long as you configure a provider.

The only reason to have not done it already is to keep the API simple and work out how to surface it (eg what the option should look like, how flexible does it need to be, and if it makes sense to group session configuration options together).

Probably the caveat I'm shooting for is is that NextAuth.js will be the point that needs to generate the token (as it is handling sign in and auth verification happens) and other apps will need to respect that format to an extent.

However callback would provide a way to customise the data and JWT handling in the cookie somewhat - within limits of what is reasonable to fit in a JWT cookie. I'm not sure if this would be in for the initial release of 2.0 (which I'm still hoping is over this weekend) but could be easy to follow up with in the next week or so.

Questions very welcome!

positonic commented 4 years ago

Probably the caveat I'm shooting for is is that NextAuth.js will be the point that needs to generate the token (as it is handling sign in and auth verification happens) and other apps will need to respect that format to an extent.

This makes it impossible for people with existing authentication apis?

Under your approach, the only way for me to integrate existing users with passwords is to create an OAuth server, and integrate it with next-auth?

iaincollins commented 4 years ago

This makes it impossible for people with existing authentication apis?

Can you give an example of how you envisage using NextAuth.js in this scenario?

e.g.

On the surface of it, if you don't need a sign in flow as you already have an authentication API, it sounds like all you need is custom /api/session endpoint that reads the JWT from req.cookies and validates it and to call that endpoint from a swr() hook on the front end.

Under your approach, the only way for me to integrate existing users with passwords is to create an OAuth server, and integrate it with next-auth?

Signing in with a username and password would require a provider of type: 'credentials'

This was supported in v1, but only type: 'oauth' and type: 'email' providers are currently supported in v2. Support fortype: 'credentials' (e.g. username / password / 2FA token / secret key / etc) is planned for v.2.1.

Being able to sign in with a username and password is separate to using JWT as far as NextAuth.js is concerned. Both features are planned - and probably JWT sessions will come fist - but are independent of each other - so you can use either a JWT or database session with any provider (0Auth or with username and password).

positonic commented 4 years ago

Can you give an example of how you envisage using NextAuth.js in this scenario?

I have an endpoint that accepts a username and password and returns a JWT. I'm just trying to figure out if and how I can use that with next-auth.

Signing in with a username and password would require a provider of type: 'credentials'

This sounds like the way for my use case

Being able to sign in with a username and password is separate to using JWT as far as NextAuth.js is concerned. Both features are planned - and probably JWT sessions will come fist - but are independent of each other - so you can use either a JWT or database session with any provider (0Auth or with username and password).

Will JWT session persist the credentials to the database too, or how will that work?

iaincollins commented 4 years ago

Can you give an example of how you envisage using NextAuth.js in this scenario?

I have an endpoint that accepts a username and password and returns a JWT. I'm just trying to figure out if and how I can use that with next-auth.

Ah! Thank you. I am also trying to figure out the edges of how it might be useful in different use cases (both as it is or with minor changes - and in the future, given further development and features on the roadmap).

Will JWT session persist the credentials to the database too, or how will that work?

I am thinking the JWT would be entirely local to the client (and not a hybrid; sessions would be JWT based session or a database based session, but not both). A hybrid approach is perfectly reasonable, I'm just thinking that most people would probably want one or the other.

I think people would need to be able to customise what is a JWT for it to be useful, and I do envisage that would be possible - with callback functions for JWT creation and for verification / updating.

I am very interested in learning about and exploring use cases people have.

positonic commented 4 years ago

For info, I'm attempting to implement a 'credentials' type...

positonic commented 4 years ago

@iaincollins I have been working on a credentials solution, and would like to get your opinion on it, but I can't push to this repo, I should probably have forked the repo, but would be easier for me at this point, if you can give me access to push my branch?

iaincollins commented 4 years ago

@jamespfarrell Hi James! If you could raise it with a fork that would be ideal!

I'm closing this as JWT support has landed in next-auth@2.0.0-beta.58 and is now documented at https://next-auth.js.org

I appreciate it might not be what everyone wants out of the JWT support, but I think having some support will help flesh that out - and would ask that folks raise specific requests as new issues, as I think there are quite a few different things people mean by JWT support and different things they want out of it and it might be the case we will want to cater for some, but perhaps not all, of those cases.

e.g.

emmenko commented 4 years ago

@iaincollins Hi, thanks a lot for implementing all those nice features and improve the usage of the library!

I have a question about using JWT. Does it mean that we don't need a DB anymore? Thanks

iaincollins commented 4 years ago

@emmenko That's a great question! The answer is "sort of" and that it will be improved.

This is not terribly complicated and could potentially be something that goes in before 2.0 is released, depending on feedback.

emmenko commented 4 years ago

Thanks for the feedback. Let me give you a bit more background of the usage we have.

We have a pretty basic setup and requirements: the app is "protected" with a Google login, so that only employees of the company can access the pages. The app is also deployed to Vercel.

Because of that, since we only need the Google login, we automatically redirect to the Google page if there is no session, to avoid an intermediate login page. For that, we use this logic:

const [session, isLoadingSession] = NextAuth.useSession(props.session);
const hasSession = Boolean(session && session.user);

React.useEffect(() => {
  if (!isLoadingSession && !hasSession) {
    NextAuth.signin("google");
  }
}, [isLoadingSession, hasSession]);

Then, when the user logs in, we fetch some data from one of the API endpoints (defined in Nextjs). The API endpoints also need to be "protected" though, as we need to verify the user's session.

We do that by "manually" checking the user session:

// The API route handler
export default async (req, res) => {
  const session = await verifySession(req);
  if (!session) {
    res.statusCode = 401;
    res.json({ message: "Unauthorized" });
    return;
  }
  // normal logic
}

And the verifySession does this:

const cookieSessionName = isDev
  ? "next-auth.session-token"
  : "__Secure-next-auth.session-token";

async function verifySession(req) {
  const { getSession } = await adapter.getAdapter(options);
  const sessionToken = req.cookies[cookieSessionName];
  const session = await getSession(sessionToken);
  return session && session.userId ? session : undefined;
}

To make this possible, we NEED to have an actual database, because the cloud functions do not share the memory and thus we won't find the session.

Using an in-memory database production could also be possible. In this case, I can make an HTTP request to the "session" endpoint to "verify" the session, so that the session info stays in memory in the auth cloud function. However, whenever we deploy something, the session is obviously gone.

By using a stateless session with JWT, we most likely don't need a database, as the information is carried within the JWT. We can also "encrypt" certain information stored in the JWT (access token, etc) so that it can only be decrypted by the server code (after verifying that the JWT signature is valid).


Hopefully this gives you a better picture of how we intend to use the session. Let me know if you need more information. Thanks

NateRadebaugh commented 4 years ago

Can you give an example of how you envisage using NextAuth.js in this scenario?

I have an endpoint that accepts a username and password and returns a JWT. I'm just trying to figure out if and how I can use that with next-auth.

Sorry to resurrect this thread but did this use case get addressed? I have a data API on a separate server similar to the above that provides the JWT and I really just want to use next-auth for the season management side.

bnbon commented 3 years ago

@NateRadebaugh Did you ever get an answer/confirmation? I too have a separate API which returns a token, so I only need the client side support, currently I am using my own implementation, but would rather not.

NateRadebaugh commented 3 years ago

@bonbonio nope so far we're not using this library for the above reasons

DragoshDX commented 3 years ago

@bonbonio nope so far we're not using this library for the above reasons

It seems that a lot of use cases are exactly this.

External API that returns a user object and a token when validated. And the next.js backend surely can consume and authorize with those APIs, but what then?

I do use the credentials provider talked about in the thread, but, it just feels silly to encode an external JWT inside the auth JWT (which would work, but is quite inelegant)

I still haven't decided on an auth solution, I'm watching this thread.

JanRuettinger commented 3 years ago

I am in the same boat as you guys. My backend returns a JWT token and I only need the client-side management of NextAuth to manage that token. I am building something custom now.