Next-auth: ๐Ÿ“ข NextAuth.js 2.0 Announcement

Created on 3 May 2020  ยท  64Comments  ยท  Source: nextauthjs/next-auth

Announcement

A lot has changed in Next.js since NextAuth 1.0 was released.

Now.sh, Vercel (formerly known Zeit), React and Node.js have also evolved a lot of the last couple of years.

This has made for a difficult moving target and so ,due the dramatic nature of those changes over the last year, feature development was paused on NextAuth (with the exception of minor maintenance updates for security).

With API routes introduced in Next.js 9.0 and Serverless deployments now the default on Now.sh (and widely supported on AWS, GPC and Azure) it's time for an update.

Authentication continues to be a pain point for a lot of folks. oAuth standardisation has improved, but common providers (Twitter, Facebook, Google, GitHub) all continue to have divergent implementations.

Lessons learned from 1.x and feedback dozens of issues and left by hundreds of people has gone into version 2.0 to try and take the pain out of setup and configuration, and to provide a solution that works for more people by improving the backend database support.

I'd like to thank everyone who raised issues, left feedback, helped out other folks when I didn't have time to get back to people, and to all those folks who raised pull requests (for all those that don't get merged in, please know they were not in vain, they've been invaluable!).

Version 2.0 is expected to drop sometime in May 2020, with releases will be published to next-auth@canary starting the first week of May. Feel free to ask questions or make requests below.

* NextAuth is not associated with Next.js or Vercel.

What's new

Version 2.0 is a complete re-write, designed from the ground up for serverless.

  • Built for Serverless - unlike version 1.x it doesn't depend on Express or PassportJS (but is compatible with them) and is designed to support automatic code splitting at build time for optimal bundle size and performance.
  • Supports the same oAuth 1.x and oAuth 2.x and email authentication flows as version 1.x (both client and server side).
  • Simple configuration with out-of-the-box support for common oAuth providers and databases.

If you are familiar with version 1.x you will appreciate the much simpler and hassle free configuration, especially for provider configuration, database adapters and much improved Cross Site Request Forgery token handling (now enabled by default for next-auth routes only).

Additional options and planned features will be announced closer to release.

What to expect

Configuration is much simpler and more powerful than in NextAuth 1.0, with both SQL and Document databases supported out of the box. There are predefined models for Users and Sessions, which you can use (or extend or replace with your own models/schemas).

Server

To add next-auth to a project, create a file to handle authentication requests at pages/api/auth/[...slug.js]:

import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
import Adapters from 'next-auth/adapters'

const options = {
  site: process.env.SITE_NAME || 'http://localhost:3000',
  providers: [
    Providers.Twitter({
      clientId: process.env.TWITTER_CLIENT_ID,
      clientSecret: process.env.TWITTER_CLIENT_SECRET,
    }),
    Providers.Google({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET
    }),
    Providers.GitHub({
      clientId: process.env.GITHUB_CLIENT_ID,
      clientSecret: process.env.GITHUB_CLIENT_SECRET
    }),
  ],
  adapter: Adapters.Default()
}

export default (req, res) => NextAuth(req, res, options)

All requests to pages/api/auth/* (signin, callback, signout) will now be automatically handed by NextAuth.

Client

You can now use the can use the useSession() hook to see if a user is signed in.

import NextAuth from 'next-auth/client'

export default () => {
  const [session, loading] = NextAuth.useSession()

  return <>
    {!loading && session && <p>Logged in as {session.user.name || session.user.email}.</p>}
    {!loading && !session && <p>Not logged in.</p>}
  </>
}

This is all the code you need to add support for signing in to a project!

Server Side Rendering

Authentication with Server Side Rendering is also supported.

import NextAuth from 'next-auth/client'

export default ({ session }) => <>
  {session && <p>You are logged in as {session.user.name || session.user.email}.</p>}
  {!session && <p>You are not logged in.</p>}
</>

export async function getServerSideProps({req}) {
  const session = await NextAuth.session({req})
  return {
    props: {
      session
    }
  }
}

You can use this method and the useSession() hook together - the hook can be pre-populated with the session object from the server side call, so that it is avalible immediately when the page is loaded, and updated client side when the page is viewed in the browser.

You can also call NextAuth.session() function in client side JavaScript, without needing to pass a req object (it is only needed when calling the function from getServerSideProps or getInitialProps).

Authentication between the client and server is handled securely, using an HTTP only cookie for the session ID.

Important! The API for 2.0 is subject to change before release.

Configuration

Configuration options are passed to NextAuth when initalizing it (in your /api/ route).

The only things you will probably need to configure are your site name (e.g. 'http://www.example.com'), which should be set explicitly for security reasons, a list of authentication services (Twitter, Facebook, Google, etc) and a database adapter.

An "Adapter" in NextAuth is the thing that connects your application to whatever system you want to use to store data for user accounts, sessions, etc. NextAuth comes with a default adapter that uses TypeORM so that it can be be used with many different databases without any configuration, you simply add the database driver you want to use to your project and tell NextAuth to use it.

Simple Example

To use SQLite in memory database (useful for development and testing, and to check everything is working):

  1. Install the database driver as a dependancy in the usual way - e.g. npm i sqlite3
  2. Pass a TypeORM configuration object when calling NextAuth() in your API route.

e.g.

adapter: Adapters.Default({
  type: 'sqlite',
  database: ':memory:'
}),

You can pass database credentials here securely, using environment variables in your application.

See the TypeORM configuration documentation for more details about supported options.

Customization

NextAuth now auto-generates simple, unbranded authentication pages for handling Sign in, Email Verification, callbacks, etc.

These are generated automatically with the appropriate sign in options based on the supplied configuration, but you can still create custom authentication pages if you would like to customize the experience.

More information

enhancement

Most helpful comment

Update for Monday, 26 May 2020

Get some sleep, @iaincollins , it's Tuesday already :rofl:

Awesome job, anyway! Thank you!!

All 64 comments

Update for Tuesday, 5 May 2020

  • Added more details about database support and schemas.
  • Added session support - now working end to end for the primary flow of signing in, obtaining secure cookie (HTTP only) and able to fetch an AccessToken for client side requests.
  • Added (optional) automatically generated sign in page - see screenshot below.

These pages are served from the API routes (e.g. /api/auth/signin) and are entirely server side generated with Preact to keep them as lightweight as possible, while still being easy to maintain.

While you can still create you own custom authentication up pages (and it will be possible to customise and/or disable these auto-generated pages) this means you don't need to do anything except set up a single API route as above, to get authentication working on your site.

The built in authentication pages are fully responsive, do not require JavaScript, have no external dependancies and use a tiny amount of embedded CSS. They can be safely embedded as an iFrame on another page. Only options you have enabled are displayed on the sign in page.

Screenshot 2020-05-05 at 18 27 59

@iaincollins any chance for a canary code drop soon? Would love to see how you're looking to put this together.. (even if it's rough)

@gidich Possibly the end of this week if things go well (otherwise, I think the end of next week).

I'd like to do testing with the models across range of databases (MySQL, MongoDB, sqlite) before encouraging anyone to check it out. I'd like to try it out in production on a microsite to shake things down.

In the meantime, you can check out the development branch at https://github.com/iaincollins/next-auth/tree/next-auth-2 to get an idea of how things are doing.

I'll keep posting updates to this thread - both updating the main post and a summary of recent work in the comments.

Update for Wednesday, 6 May 2020

  • Improved NextAuth 2.0 client
  • Comes with useSession() hook
  • Provies static method for NextAuth.Session()
  • Uses HTTP only session cookie for authentication
  • Cookie names and options are all customisable (h/t @rxl881)

Hooks

import NextAuth from 'next-auth/client'

export default () => {
  const [session, loading] = NextAuth.useSession()

  return <>
    {!loading && session && <p>Signed in as {session.user.name || session.user.email}.</p>}
    {!loading && !session && <p>Not signed in.</p>}
  </>
}

This is all the code you need to add support for signing in to a project!

Static Method (isomorphic)

You can also call NextAuth.session(), both client and server side.

import NextAuth from 'next-auth/client'

export default ({ session }) => <>
  {session && <p>Signed in as {session.user.name || session.user.email}.</p>}
  {!session && <p>Not signed in.</p>}
</>

export async function getServerSideProps({req}) {
  const session = await NextAuth.session({req})
  return {
    props: {
      session
    }
  }
}

Update for Thursday, 7 May 2020

Although tagged as a beta release, it's really a developer preview release.

The beta releases should not be used in production, they will contain glaring bugs, may not be secure and there will be no upgrade path to future releases.

However, if you are interested in seeing how things are going and would like to provide early feedback or just kick the tires on the next version you can check it out.

Feedback is welcome, either here, or as regular issues - please mark them as being relevant to version 2.0.

Update for Friday, 8 May 2020

  • Added CSRF token and CSRF token API end point for clients.
  • CSRF token from clients matched against a cryptographic hash generated with a secret (which acts as a salt) stored in a server-only, secure, host-only cookie.
  • Improved default security of cookies, using Secure option, setting __Secure- and __Host- prefixes on cookie names (these can be customised) and signing of cookies.
  • Cookie restrictions and prefixes are relaxed by default on non HTTPS urls (e.g. http://localhost:3000) for developer convenience.
  • Completed secure 'Sign out' flow, with sign out endpoint at /api/auth/signout.
  • Improved callback URL verification (used on sign in and sign out flows). Defaults to only allowing same site URLs, but can specify an async function in options to handle this however you want.
  • Improved behaviour and accessibility of the default 'Sign in' page.
  • Published [email protected]
  • Updated the preview at https://next-auth-example.now.sh

CSRF Token

Cross Site Request Forgery protection was the focus of this release.

Improvements over NextAuth 1.0 include CSRF token protection for sign in (without pre-sessions), no need to persist CSRF tokens server side, cryptographic verification of token authenticity, and stricter default settings for cookies, especially the HTTP only, secure, host-only CSRF token.

The Double Submit Cookie method combined with the __Host- prefix and using a hash of the cookie value generated by the value combined with a secret known only to the server provides a reasonable starting point for protection against Cross Site Request Forgery attacks without negatively impacting the user experience or requiring the creation of server side sessions for clients that are not authenticated.

~There is an open question as to what the most appropriate behaviour for users should be in the event of a mismatch (i.e. what should be displayed to the client), which is pending seeing what the user experience is like in practice and further documentation is needed. Fortunately the approach taken makes it unlikely for mismatch errors to occur in practice.~

What happens when CSRF tokens are invalid is left up to individual routes, which are passed a csrfTokenValid boolean to simplify secure handling while delivering optimal user experince.

e.g. a POST request to /api/auth/signout does not contain a valid token (i.e. that matches the signed cookie in the HTTP headers) it simply sets a new cookie and redirects the client to make a GET request for /api/auth/signout which displays a sign out button in a secure form that does contains a valid token.

Early release?

I followed up some the initial update for today with more refactoring, added sign out functionality, release as beta 10 and updated the post above.

Currently, these are the "missing critical features" that I consider are blocking release of 2.0:

  • Handle Session and AccessToken expiry (and token rotation).
  • Create account deletion endpoint.
  • Clone oAuth provider profile for Facebook (currently: Twitter, Google, GitHub, oAuth 1 & 2).
  • Testing (in particular with MongoDB, MySQL and MariaDB).
  • Quality improvements to sign in / sign out pages.
  • More comprehensive documentation.

I've managed to blitz through 2.0 in a few days and I don't anticipate any these being particularly difficult. I could probably complete them over the next couple of days and could release 2.0 early in a few days.

There are of course still other features missing to meet / exceed feature parity with NextAuth 1.0 and cover various use cases, but personally for me they are not as blocking:

  • Email sign in flow (similar to NextAuth 1.0, but improved DX and with additional security).
  • Credentials sign in flow (e.g. username/password; possible in NextAuth 1.0 but not supported out of the box).
  • Add session caching to next-auth/client.
  • Improved error handling and audit logging (e.g. callbacks).
  • Improved auditing (e.g. event callbacks for logging).
  • Option for JWT stateless tokens, instead of storing sessions in a database - great for prototyping and some specific use cases, but of limited use if you don't at least have a user database of some kind.
  • User management interface to add, remove, update users.

Interested in feedback on the possibility of releasing early.

The current version of NextAuth 1.0 isn't viable for Next.js 9 or hosting on the now.sh anymore and is of limited use (and it will not be actively supported). With that in mind, I am leaning towards releasing 2.0 as soon as it is viable.

I am considering releasing it even without the email sign in flow (which would then follow later, but would essential the same way as the other providers) or username / password based logins.

Overall, I think it's going to be quicker and easier to address issues the sooner 2.0 is released, but the tradeoff is that some things on the wish list might take a bit longer if other things turn out to be more important to people in the short term.

A big difference with 2.0 is changes don't impact your entire application, and are restricted to the /api/auth route (and the route name is configurable), so it's a lot easier and safer to upgrade and roll out changes, so it really should be quicker to roll out features - but I'd also like to take the time with the email flow and roll out something that works really well (e.g. nice HTML email templates, secure token verification, etc).

Please feel free to vote up or down on this comment as a way to leave feedback, or leave your own comment below if you feel a particular way about releasing early with a more limited feature set (and building on it over time).

This looks like a very nice library and I want to integrate it into a project I'm starting, but is it possible to add custom providers?

is it possible to add custom providers?

It is! I was actually just talking about that in this thread, the documentation doesn't cover this yet, but this is a quick breakdown:

It comes with Twitter, GitHub, Google 'provider' profiles - with at least Facebook and maybe Keycloak to follow. Provider configs are passed in an array called providers:

import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
import Adapters from 'next-auth/adapters'

const options = {
  site: process.env.SITE_NAME || 'http://localhost:3000',
  providers: [
    Providers.Google({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET
    }),
  ],
  adapter: Adapters.Default()
}

export default (req, res) => NextAuth(req, res, options)

All these functions actually do is return a JSON object, with any options passed to the functions overriding the default values for that provider profile.

e.g. if you do console.log() on the response from Providers.Google() you get this:

{
  id: 'google',
  name: 'Google',
  type: 'oauth',
  version: '2.0',
  scope: 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email',
  options: { grant_type: 'authorization_code' },
  accessTokenUrl: 'https://accounts.google.com/o/oauth2/token',
  requestTokenUrl: 'https://accounts.google.com/o/oauth2/auth',
  authorizationUrl: 'https://accounts.google.com/o/oauth2/auth?response_type=code',
  profileUrl: 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json',
  profile: (profile) => {
    return {
      id: profile.id,
      name: profile.name,
      email: profile.email,
      image: profile.picture
    }
  },
  clientId: '',
  clientSecret: ''
}

So, in theory you can pass your own configuration object here for an oAuth 1.0, 1.0A or 2.0 provider and it should work, as long as the options and URLs are correct.

If you want to use another oAuth provider, or you can't get one to work no matter what options you try (both the documentation and error logging for debugging could be better) I'm happy to help!

I'm also happy to include built-in support for common providers if folks are able to provide configuration options for them. :-)

@iaincollins nice! Going to try it out.

Okay, I tried it with Twitch and it works. But what's the callback URL that I have to add to the settings? Now I'm getting redirect mismatch when I use a custom URL so I suppose it's also provided by the library? I tried /api/auth/callback and /api/auth/twitch/callback too.

Edit: Deleted and changing my reply as I got the wrong end of the stick and answered the wrong question and just created confusion, please ignore that. ๐Ÿ˜‚

If you go to /api/auth/providers you should see a list of what options for each provider (although it's a JSON object so will may to pretty print it to read it).

The path for the oAuth callback should be /api/auth/callback/twitch

The order of items in the path is slightly confusing (usually it would be as you've written it and I actually got stuck on this when I got it wrong configuring one of my own apps) but this order is a bit easier to support with the way Next.js API routes work, which is why it's like that.

PS: If you get a Twitch config working would love to have a copy of it and make it supported profile!

Thanks for this! Iโ€™ll be trying it out with https://github.com/danielcondemarin/serverless-next.js ๐Ÿ™‚

Thanks for this! Iโ€™ll be trying it out with https://github.com/danielcondemarin/serverless-next.js

Oh awesome! I'm a big fan of that serverless component and would appreciate feedback on if NexAuth works with it or if there are any issues.

v2 has very few dependencies but I worry that the default TypeORM adapter might be too much for serverless at the edge limits, if tree shaking isn't able to get the size down enough.

If that is the case, I'd be happy to consider creating an alternate lightweight adapter (maybe for a specific database?).

Oh awesome! I'm a big fan of that serverless component and would appreciate feedback on if NexAuth works with it or if there are any issues.

v2 has very few dependencies but I worry that the default TypeORM adapter might be too much for serverless at the edge limits, if tree shaking isn't able to get the size down enough.

If that is the case, I'd be happy to consider creating an alternate lightweight adapter (maybe for a specific database?).

Thanks! I will feedback for sure.

The Next core team recently released a new target called experimental-serverless-trace which Vercel Now uses for serverless deployments. For lambda deployments is better because unlike the serverless target it doesn't bundle node_modules for each serverless page. Instead you end up with a "shared" node_modules folder you can upload to a Lambda Layer for example.
I am working on adding support for it in sls-next.js which should solve the edge limit issues.

Update for Saturday, 9 May 2020

Thank you to @LoriKarikari for contributions and testing of both oAuth and database integration and for code contributions.

I'm thrilled to get external feedback that database integration seems to be working as expected (after a little bug squashing!) and to see that integration with additional oAuth services works.

Thanks folks have have left feedback here and elsewhere, I'm glad to hear it seems like v2 is going in the right direction and look forward to being able to getting to state where we have a production release in the coming days/weeks.

Just wanted to pop in and say this is looking great so far. Been trying it out with prisma2 for storing the sessions etc and works fine with a custom adapter.


Here's a recipe if anyone wants to try it

Given this schema.prisma:

model User {
  id            String         @default(uuid()) @id
  email         String         @unique
  avatarUrl     String?
  name          String?
  sessions      Session[]
  accounts      Account[]
  createdAt     DateTime       @default(now())
}

model Session {
  id                 String   @default(uuid()) @id
  userId             String
  user               User     @relation(fields: [userId], references: [id])
  accessToken        String   @default(cuid()) @unique
  accessTokenExpires DateTime
  expires            DateTime
  createdAt          DateTime @default(now())
}

model Account {
  id                 String    @default(uuid()) @id
  userId             String
  user               User      @relation(fields: [userId], references: [id])
  providerId         String
  providerType       String
  providerAccountId  String    @unique
  refreshToken       String?
  accessToken        String
  accessTokenExpires DateTime?
  createdAt          DateTime  @default(now())
}

You can make an adapter like this:

import { PrismaClient, User } from '@prisma/client'

interface Profile {
  name: string
  email: string
  image: string
}

const PrismaAdapter = () => {
  function debug(...args) {
    if (process.env.NODE_ENV === 'development') console.log(...args)
  }
  let connection = new PrismaClient()

  async function getAdapter() {
    if (!connection) {
      connection = new PrismaClient()
    }

    // Called when a user signs in
    async function createUser(profile: Profile) {
      debug('Create user account', profile)
      return connection.user.create({
        data: {
          name: profile.name,
          email: profile.email,
          avatarUrl: profile.image,
        },
      })
    }

    async function updateUser(user: User) {
      debug('Update user account', user)
      return new Promise((resolve, reject) => {
        // @TODO Save changes to user object in DB
        resolve(true)
      })
    }

    async function getUserById(id = '') {
      debug('Get user account by ID', id)
      return connection.user.findOne({ where: { id } })
    }

    async function getUserByProviderAccountId(providerId: string, providerAccountId: string) {
      debug('Get user account by provider account ID', providerId, providerAccountId)
      return connection.account
        .findOne({
          where: { providerAccountId },
        })
        .user()
    }

    async function getUserByEmail(email: string) {
      debug('Get user account by email address', email)
      return new Promise((resolve, reject) => {
        // @TODO Get user from DB
        resolve(false)
      })
    }

    async function getUserByCredentials(credentials: string) {
      debug('Get user account by credentials', credentials)
      return new Promise((resolve, reject) => {
        // @TODO Get user from DB
        resolve(true)
      })
    }

    async function deleteUserById(userId: string) {
      debug('Delete user account', userId)
      return new Promise((resolve, reject) => {
        // @TODO Delete user from DB
        resolve(true)
      })
    }

    async function linkAccount(
      userId: string,
      providerId: string,
      providerType: string,
      providerAccountId: string,
      refreshToken: string,
      accessToken: string,
      accessTokenExpires: string
    ) {
      debug(
        'Link provider account',
        userId,
        providerId,
        providerType,
        providerAccountId,
        refreshToken,
        accessToken,
        accessTokenExpires
      )
      return connection.account.create({
        data: {
          accessToken,
          refreshToken,
          providerAccountId,
          providerId,
          providerType,
          user: {
            connect: {
              id: userId,
            },
          },
          accessTokenExpires,
        },
      })
    }

    async function unlinkAccount(userId: string, providerId: string, providerAccountId: string) {
      debug('Unlink provider account', userId, providerId, providerAccountId)
      return new Promise((resolve, reject) => {
        // @TODO Get current user from DB
        // @TODO Delete [provider] object from user object
        // @TODO Save changes to user object in DB
        resolve(true)
      })
    }

    async function createSession(user: User) {
      debug('Create session for user', user)
      const date = new Date()
      const sessionExpiryInDays = 30
      const accessTokenExpiryInDays = 30
      return connection.session.create({
        data: {
          accessTokenExpires: new Date(
            date.setDate(date.getDate() + accessTokenExpiryInDays)
          ).toISOString(),
          expires: new Date(date.setDate(date.getDate() + sessionExpiryInDays)).toISOString(),
          user: {
            connect: {
              id: user.id,
            },
          },
        },
      })
    }

    async function getSessionById(id = '') {
      debug('Get session by ID', id)

      return connection.session.findOne({ where: { id } })
    }

    async function deleteSessionById(id: string) {
      debug('Delete session by ID', id)
      return connection.session.delete({ where: { id } })
    }

    return Promise.resolve({
      createUser,
      updateUser,
      getUserById,
      getUserByProviderAccountId,
      getUserByEmail,
      getUserByCredentials,
      deleteUserById,
      linkAccount,
      unlinkAccount,
      createSession,
      getSessionById,
      deleteSessionById,
    })
  }

  return {
    getAdapter,
  }
}

export default PrismaAdapter

Iain, hi! Thank you very much! I've tried to use next-auth-2 with mongodb. I've installed mongodb driver and change adapter.config.js to this:
export default { type: 'mongodb', database: 'my-database2', };
When I click the signin link, I get an error:
image

And in the database entry appears:
image

Iain, hi! Thank you very much! I've tried to use next-auth-2 with mongodb. I've installed mongodb driver and change adapter.config.js to this:
export default { type: 'mongodb', database: 'my-database2', };
When I click the signin link, I get an error:

It's probably related to the id field but I could be wrong.

@Fumler RE: Prisma driver.

That's great, thank you! There will probably be some breaking changes to the adapter in future, but they won't be that complicated - maybe a slight change in method names and response object.

If you want to raise an issue with this driver in - even a PR if you wanted to drop it in src/adapters/prisma.js (doesn't matter if not complete / tested!) - that would be really neat.

I think we can probably find a way to update how things work so that that loading (e.g. TypeORM or Prisma (or whatever) are loaded depending on what people choose, without making things too complicated. If not ,we can always split them out, but it would be interesting to have.

@nikitalk Thanks, I've turned your post into an issue as it sounds like a bug in the current beta!

@Fumler RE: Prisma driver.

That's great, thank you! There will probably be some breaking changes to the adapter in future, but they won't be that complicated - maybe a slight change in method names and response object.

If you want to raise an issue with this driver in - even a PR if you wanted to drop it in src/adapters/prisma.js (doesn't matter if not complete / tested!) - that would be really neat.

I think we can probably find a way to update how things work so that that loading (e.g. TypeORM _or_ Prisma (or whatever) are loaded depending on what people choose, without making things too complicated. If not ,we can always split them out, but it would be interesting to have.

I was considering doing a PR for it, but decided against it since the adapter won't work out of the box, it requires a very specific data model for prisma as you can see in the schema file. This might be better to include as something in the README or wiki or whatever as a recipe. But I'm not sure.

@iaincollins man you are a legend!

Been banging head with AWS Cognito/Amplify for a few days and realised how 100x complicated and poorly documented it was, and searching for a simple solution, and here you are with a great looking 2.0 about to drop.

Blocker for me would be lack of Credentials support e.g. sign up, sign in, forgot password, reset password etc.
I don't need a UI for those, just the endpoints or internal API.

Having built identity management func a few times, I certainly would prefer not to do it again, and the out of box Next.js integration is a winner here.

@iaincollins any news on customizing the sign-in pages yet?

Btw my first migration to v2 of next-auth worked flawlessly. Thanks a lot for the quick work!

Update for Wednesday, 13 May 2020

  • NextAuth now supports using a provider + context with useSession() - thanks to @Fumler!
  • Added example of how to use in _app.js to reuse session data between components.
  • Updated example at https://next-auth-example.now to next-auth beta.22 and Next.js 9.4
  • Removed fetch polyfill and dotenv dependancies from NextAuth as included with Next.js 9.4
  • Improved NextAuth client; bug fixes and size reduction (now ~2.5k).
  • Added ESLint / Standard JS (npm run lint) and refactored to address code quality issues.
  • Improved CSRF token handling.
  • Refactored Adapaters / Models to make creating new adapters easier.
  • Begun work on email sign up and verification flow.

NextAuth.Provider

Thanks to a PR from @Fumler, you can now use a Provider pattern to re-use context between components, reducing network calls and improving performance.

Adding NextAuth.Provider to _app.js in Next.js is very straightforward:

import NextAuth from 'next-auth/client'

export default ({ Component, pageProps }) => {
  return (
    <NextAuth.Provider>
      <Component {...pageProps} />
    </NextAuth.Provider>
  )
}

Accessing a session in a component is unchanged; it 'just works' whether you are using NextAuth.Provider or not:

import NextAuth from 'next-auth/client'

export default () => {
  const [session, loading] = NextAuth.useSession()

  return <>
    {loading && <p>Checking sessionโ€ฆ</p>}
    {!loading && session && <p>Signed in as {session.user.name || session.user.email}.</p>}
    {!loading && !session && <p><a href="/api/auth/signin">Sign in here</p>}
  </>
}

oAuth

Thanks to @LoriKarikari we have a bunch new additional oAuth services in progress!

  • Mixer
  • Discord
  • Slack
  • Reddit

I had to do some work on the new Twitch flow as they changed the API in production less than a week after we added it. ๐Ÿคฆโ€โ™‚๏ธ oAuth APIs change commonly enough that collectively they are a moving target.

With that in mind, I might move all the oAuth code natively into NextAuth which will reduce dependancy size even more (as we only need to implement what we need) and make addressing issues as they arise easier; right now we are having to monkey patch node-oauth to the point it's probably no longer work using it (but it's been invaluable so far and I'm very grateful for it).

Email

I've started work in earnest on the email flows this week.

There isn't a huge amount do, and it's much improved over NextAuth 1.x.

  • This time email invites are sent out tracked in a separate 'invites' table.
  • User accounts are only created after a user accepts an invite.
  • Invite tokens are also stored properly hashed (so can't be exploited even by someone who manages to get a hold of a database dump) and with an enforced expiry time.
  • For a better user experience, much nicer HTML emails will be supported out of the box.

Notes

This update has been delayed a few days was hoping to complete some work I sadly haven't had time for this week so far. Things are still on track for releasing 2.0 this month, but it looks like I'm probably going to wait till the email flow is complete first.

Having built identity management func a few times, I certainly would prefer not to do it again, and the out of box Next.js integration is a winner here.

Thanks, that's great to hear!

Just FYI longer term plans include a simple admin UI to manage users (search, create, update, delete, etc) and the option to control who can sign in / sign up more easily (e.g. must already have an account created in the DB for them, or based on their email domain, etc).

I don't actually see a reason NextAuth couldn't be used for non-Next.js apps in future, for those interested in a good auth solution generally.

e.g. An actual Next.js app might still handle sign in callbacks (etc) but as it is, it can be configured to allow any site on the same domain to call fetch('/api/auth/session') securely (though for optimal security it defaults to only supporting requests from the same host).

@iaincollins any news on customizing the sign-in pages yet?

No, I will probably leave supporting them till after email signup flow is done as it will probably be ultimately less work to do it that way, but the email signup flow is now in progress.

This should be super easy though! They will just be options when calling NextAuth in the /api/auth/* route, for example:

pages: {
  signup: 'https://signup.example.com/',
  error: '/error'
}

I expect that 'signup', 'signout', 'error', 'invite' (for email sign up flow) will all be available and can independently be defined (or not), as either absolute or relative URLs. When defined, any requests for those URLs on /auth/api/* will simply redirect to the specified location. Error pages will be passed error codes via query parameters (these will need documenting when they are settled on).

Btw my first migration to v2 of next-auth worked flawlessly. Thanks a lot for the quick work!

That's awesome! I'm trying to keep the beta releases decently stable, even if they are missing features. The current beta.22 is pretty good I think, but sometimes they are a little buggy (usually if I've just released it for testing and not spotted a bug until after it has gone out).

I know there are issues with getting the session on the first call to the client still (and erasing it properly in the client on logout), which will be resolved in future; the client behaviour needs a bit more edge case supporting complexity ported over from NextAuth 1.0.

PS: For user accounts created in v1 they will end up with some old objects on them that can be deleted (e.g. user.twitter = {}, user.facebook = { ... }) as these are stored in a separate table/collection now; but they won't do any harm as they are not used now.

Just FYI longer term plans include a simple admin UI to manage users (search, create, update, delete, etc) and the option to control who can sign in / sign up more easily (e.g. must already have an account created in the DB for them, or based on their email domain, etc).

I don't actually see a reason NextAuth couldn't be used for non-Next.js apps in future, for those interested in a good auth solution generally.

Obviously do what you want, but I have my own UI, separate from business logic,
and plan on using NextAuth code for out-of-box key workflow and provider support, and oob Next.js integration,
and don't want any of your opinionated UI ๐Ÿ˜

Might be a good idea to make a core package with just the library which you can add to your app with custom UI. And create a separate package with pre-built UIs which are optional?

@TimNZ Thanks, that's helpful feedback. If it turns out that folks don't care about that no sense in lumbering everyone with it.

@LoriKarikari +1 That seems like a good approach.

Maybe the next-auth-example project could even have pages/admin folder, allowing folks to copy it and customise it if they want something like that.

@iaincollins yeah, I would focus on the core with custom UI first because I think more people will use custom UIs anyway. And if they need prebuilt UIs they can copy them from the example app. Like now there is no way yet for custom sign up pages which will be a deal breaker for a lot of people.

Yeah I think the prebuilt basic login / signup pages are a great feature for beginners, but anyone else would most likely be wanting to at least design their own pages.

@TimNZ Thanks, that's helpful feedback. If it turns out that folks don't care about that no sense in lumbering everyone with it.

Hey - if Forest Admin can raise $7m for an Admin Framework, why can't Next Auth be a great starter and maybe develop into something something, if that's of interest.

https://techcrunch.com/2019/11/28/forest-admin-raises-7-million-to-help-you-build-admin-panels/
https://www.forestadmin.com/

I abandoned Cognito, and not interested in Auth0 and others, because I envitably want to customise everything front and back.
Advantage of using NextAuth is I can do that with a solid foundation, and I get the serverless for scale/reliability for free.

@iaincollins again thanks so much for this.

Let me know how I can be of any help.

Update for Friday, 15 May 2020

  • Released [email protected].
  • Updated example at https://next-auth-example.now.sh/
  • Now supports email sign in flow.
  • Supports HTML and plain text emails out of the box.
  • Fully customisable from, subject and HTML/text body (with parameters for dynamic emails).
  • Supports adding dynamic unsubscribe option (defaults to {{unsubscribe}} short code supported by mail service providers like sendpulse).
  • Uses the latest version of nodemailer.com for robust email support.
  • Email tokens stored securely, so that they cannot be used even if the database is compromised.
  • Email accounts are not created until _after_ a user has verified their email address.
  • Email sign in is configured just like any other provider.
  • Please note there are potentially breaking changes in recent betas, specifically to session schema and DB initialisation.
  • Database schemas no longer auto-configured at run time (this will return, in a safe fashion), the example has been updated to account for this.

Known issues

  • As with sessions, expiry values on email invites are not yet enforced.
  • Not all user journeys are well supported yet (mostly edge cases).
  • ~The SMTP service that https://next-auth-example.now.sh/ is currently using can be a little slow and might be flakey (it is a new SMTP account and is currently being rate limited).~ resolved

Simple Example

const options = {
  site: process.env.SITE,
  providers: [
    Providers.Email({
      server: process.env.EMAIL_SERVER, // e.g. "smtp://user:[email protected]:25"
      from: process.env.EMAIL_FROM
    }),
    Providers.Twitter({
      clientId: process.env.TWITTER_ID,
      clientSecret: process.env.TWITTER_SECRET
    }),
    Providers.Google({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_SECRET
    }),
  ],
  adapter: Adapters.Default(adapterConfig),
}

Screenshots

The goal of the default look and feel is a white labelled experience, it will be subject to further refinement prior to release.

The email is fully customisable with the default adapter, but you can also write your own adapter to handle email account creation and verification however you want.

Signup

Signup

Check Email

Check Email

Email

Email

I'm trying to drop the adapterConfig object / Adapters config entirely but it doesn't seem to be possible. No matter what I do its always looking for that getAdapter() function.

Anyway to drop that if you dont have any local auth method?

Btw keep up the great work! This is already behaving so much better than v1 ever did in my projects :)

@iaincollins damn you've been on a roll!

I'm trying to drop the adapterConfig object / Adapters config entirely but it doesn't seem to be possible. No matter what I do its always looking for that getAdapter() function.

Anyway to drop that if you dont have any local auth method?

Not right now; currently a database of some kind is always needed and used to store users and sessions, so a backend adapter is required (with the default version being TypeORM, as it supports a range of popular SQL and noSQL databases).

However, in a future update I'm considering providing the option to support a JWT token for those with use cases that don't need any kind of server side state. It could be done with minimal (and entirely backwards compatible) changes.

It would be possible to support a flow that verifies a user was signed in with a particular third party oAuth service and to make those sessions persistent entirely in the browser securely, using encryption; but the use cases for JWT tokens with no database backend at all are a bit limited.

However, they can be useful for read-only applications; for example to require users to have accounts with a specific provider at a specific domain (e.g. Google) and control access to a company dashboard.

You could also potentially make it possible white-list email domains or specific email address on the backend, to restrict access to certain pages or features to those users (e.g. to update changes which are then saved back somewhere like GitHub, which might be useful for a simple content management system).

Btw keep up the great work! This is already behaving so much better than v1 ever did in my projects :)

Great to hear its working well so far!

There is still a bunch of stuff to shake out but we are getting close now!

Update for Saturday, 16 May 2020

_tl;dr bug fixes, improved email sign in and impending release of v2_

  • Resolved issues with email sign in with the example, you should be able to to try it out now https://next-auth-example.now.sh/
  • Improved error handling for all sign up flows and callback handlers (oAuth and Email) with much better error handling (you will still be able to specify your own error page, but now you will have more useful information about the error that has occurred via the ?error= query parameter).
  • Internal refactoring for better code quality.
  • You can now easily create your own fully customised email provider using source for the default email provider as an example.

    You can customise the messages, use a different email sending library, push messages to a queue to be sent later by another service, check the email address against a whitelist before sending them (etc). This approach also makes it easier to debug problems with sending emails.

What's next

There are about 40 items on my TODO list before release.

Most of them fall into one of the following categories:

  • Database Integration
    In particular resolving any outstanding issues with MySQL, Postgres and MongoDB.
  • Session & Verification Token Expiry
    This was left till last as I want to do it hand in hand with Database Integration as I want to be confident in the date/time handling (in particular as it will also involve syncing with client side cookies).
  • Customised Pages
    Support for custom pages is very quick and easy to add.
    Actually creating example pages is a more work).
  • Improvements to Client
    Bug fixes (e.g. ensuring session state is always correct client side after signin/signout), creating methods for getting client CSRF token to make it easier to build custom login forms, etc.
  • Documentation
    Options, in particular Provider and Adapter options need to be documented.
    Advanced topics like creating your own provider are probably okay to come later.

There are some things I'd like to do (like refactor oAuth handling to make it easier to debug) and automated testing of all flows, but they are not blockers.

I can get the first two done (database integration and token expiry) over the weekend. When they are done I will merge the current development release to the master branch (the exiting master will become v1/master so it's still technically possible to provided updates to those using it) and we may be looking at release of v2.0 next week (May 17-23).

As always, improvements, bug fixes and feedback are welcome at any point!

I'd encourage folks to raise issues - or make noise by commenting on existing issues - if they would like specific features or things addressed or have views on them (e.g. confusing, buggy, doesn't work as expected, not enough options).

I'll add the provider documentation tomorrow so you can merge it into the master branch.

@iaincollins holding off going deep until RC, as you are rapidly deving.

Your Adapter interface implementation allows everything I can think of, and it's easy to see how Credentials support will be added - code is easy to follow.

I'll be creating a custom Adapter for ArangoDB, and will likely use JWT for session token.

Update for Tuesday, 19 May 2020

Great progress against a couple of outstanding bullet points.

  • Added Facebook support (@LoriKarikari)

  • Added Auth0 (@LoriKarikari)

  • Sessions now expire 30 days from the last time a user was active
    This is configurable; they can also be set to to never expire, or to expire in the browser when the window is closed. How often the database is written to keep a session alive can also be configured.

  • Email sign in / verification links expire after 24 hours
    This is also configurable.

  • Debug mode is now an option (defaults to off)

  • New documentation! (@ndom91 @LoriKarikari)

    @ndom91 set us up with an awesome new documentation site powered by Docusaurus! at http://next-auth.js.org which will be the new homepage for the site.

    The content is still work in progress, but this is incredibly valuable and great way to place to surface some of the documentation that @ndom91 and @LoriKarikari have been working on. I'm already finding it much easier to read and review and super useful, especially the search function.

  • The contributing guide has also been improved - now also updated and pinned as an issue.

  • Updated https://next-auth-example.now.sh/

Thanks for the amazing contributions! There is quite a bit of significant work done in the last few days, I look forward to another big update fairly soon.

Update:

Added some things we did since the last update that I forgot about!

Over the next few days I plan to resolve some missing options and improve some points of edge case handling then I'm going to work on testing database integration and documentation.

I'm not quite sure about getting everything ready for the end of this week but I don't think will overshoot it by much and I think we are still on track for the original target of end of the month and, thanks to contributions, a much more ambitious and better release than I'd imagined.

I think I really underestimated the amount of documentation that was needed , so I'm so grateful for all the work done there by both @ndom91 and @LoriKarikari and am looking forward to updating it with new info and examples!

In the v2 docs it says [...slug.js] which I think should be[...slug].js

The Discord provider does not add the absolute URL to the redirect_uri query.

Discord oAuth Error:

{"redirect_uri": ["Not a well formed URL."]}

Config:

const options = {
  site: process.env.BASE_URL,
  providers: [
    Providers.Discord({
      clientId: process.env.DISCORD_OAUTH_ID,
      clientSecret: process.env.DISCORD_OAUTH_SECRET,
    })
  ],
  database: {
    type: 'mongodb',
    database: process.env.MONGODB_URI,
  }
}

oAuth URL:

https://discordapp.com/api/oauth2/authorize?response_type=code&prompt=consent&redirect_uri=%2Fapi%2Fauth%2Fcallback%2Fdiscord&scope=identify%20email&state=67ffc77&client_id=000000000000000

Hi, @Gr8z I just checked this out and it should work fine. I'm not able to reproduce this error. Which version are you using?

@LoriKarikari I am using the latest: 2.0.0-beta.43

Please disregard, the env variable change required a restart. It is working as expected.

@Gr8z haha okay, no worries! Are you getting an invalid grant_type error?

@iaincollins implemented next-auth and it works great, thanks.
I can see a bunch of enhancements I want to do in different areas.

Could you please create issues tagged to appropriate milestones (existing 2.0, or 2.1, 3.xx etc) so I can see what your roadmap is?

Don't want to duplicate efforts e.g. a pull request, and you're already doing it, or you have a strong opinion on something and you may not like my approach.

Hi, using 2.0.0-beta.45 results in the following error when logging in with Google.

ADAPTER_CONNECTION_ERROR { DataTypeNotSupportedError: Data type "timestamp" in "Account.accessTokenExpires" is not supported by "sqlite" database.
    at new DataTypeNotSupportedError (/Users/emmenko/xxx/node_modules/typeorm/error/DataTypeNotSupportedError.js:7:28)
    at /Users/emmenko/xxx/node_modules/typeorm/metadata-builder/EntityMetadataValidator.js:74:27
    at Array.forEach (<anonymous>)
    at EntityMetadataValidator.validate (/Users/emmenko/xxx/node_modules/typeorm/metadata-builder/EntityMetadataValidator.js:71:36)
    at /Users/emmenko/xxx/node_modules/typeorm/metadata-builder/EntityMetadataValidator.js:42:74
    at Array.forEach (<anonymous>)
    at EntityMetadataValidator.validateMany (/Users/emmenko/xxx/node_modules/typeorm/metadata-builder/EntityMetadataValidator.js:42:25)
    at Connection.buildMetadatas (/Users/emmenko/xxx/node_modules/typeorm/connection/Connection.js:497:33)
    at Connection.<anonymous> (/Users/emmenko/xxx/node_modules/typeorm/connection/Connection.js:127:30)
    at step (/Users/emmenko/xxx/node_modules/tslib/tslib.js:139:27)
    at Object.next (/Users/emmenko/xxx/node_modules/tslib/tslib.js:120:57)
    at fulfilled (/Users/emmenko/xxx/node_modules/tslib/tslib.js:110:62)
  name: 'DataTypeNotSupportedError',
  message:
   'Data type "timestamp" in "Account.accessTokenExpires" is not supported by "sqlite" database.' }

@emmenko Try [email protected] :-)

Update for Monday, 26 May 2020

_This will probably the last announcement before the release of 2.0_

We have merged 15 PRs in the last week and we've also been busy reviewing issues and responding to feedback. We've also been busy updating the documentation at https://next-auth.js.org

The last bit of work that remains before the release of 2.0 is additional testing to shake out issues and updating the documentation - which is in much better shape, but there are still some bits that are outdated or incomplete.

We would appreciate bug reports (and positive feedback!) as issues for MySQL, Postgres and MongoDB in particular!

The example site has more information on how to configure to configure a database.

Update for Monday, 26 May 2020

Get some sleep, @iaincollins , it's Tuesday already :rofl:

Awesome job, anyway! Thank you!!

I also wonder when he sleeps ๐Ÿ˜‚

@iaincollins Not going to implement credentials login for the release? Just wondering what the roadmap is for that. Wanted to begin working on it bit the codebase changes so fast ๐Ÿ˜‚ I'll leave it up to you and can help improving it afterwards.

I also wonder when he sleeps ๐Ÿ˜‚

I actually joined a new team a couple of weeks ago so have had to time shift my work to evenings and a bit of time over lunch breaks :D

Credentials

@iaincollins Not going to implement credentials login for the release? Just wondering what the roadmap is for that. Wanted to begin working on it bit the codebase changes so fast ๐Ÿ˜‚ I'll leave it up to you and can help improving it afterwards.

So that's a great question and I'm sure a lot of folks are interested in that feature!

I am always super happy if folks want to work on features they are interested to get things earlier. I appreciate that is quite hard right now as there is a lot of refactoring, and some more refactoring that needs to happen to make things easier to follow and there could be better documentation for contributors.

I think support for credentials - which might be username / password, but it seems like could also include something like 2FA (e.g. text message, authenticator token, etc) - is something I've been thinking of targeting for 2.1, after code cleanup and maybe some other features (see JWT).

I'm also thinking of what changes might be appropriate to accommodate making email addresses optional - if that is a thing we want to support - and if these things might relate (e.g. what should the database structure look like for them).

I'm actually somewhat reluctant to add this feature as having passwords tends to be a footgun. It was supported in v1, but you had to do quite a bit of work yourself to actually do it, and that was somewhat by design.

The reason for my reluctance that is that it exposes people to risk - lots of people don't use a password manager still, and even when they do they often still use reuse passwords of their own choosing, meaning accounts can be compromised whenever there a password leak at another organisation.

However, I appreciate a lot of people either have situations where they need something like this, because they have an existing system that works that way already, because they want to be able to do things like add 2FA or because they feel their users have an expectation of using a password based login for their service. I also appreciate it's a very commonly requested feature.

With that in mind I agree it's worth doing, and worth doing well so it's as secure as it can be out of the box (e.g. hashing with bcrypt) and easily extendable to support good practices like 2FA.

JSON Web Tokens (JWT)

On that note, another thing I'm keen to add in 2.1 is JWT support, so that sessions don't need to be stored in a database.

I'm quite keen on as 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.

Example use cases:

  • You could generate passwordless/magic links that create a JWT token to sign a user in without ever needing a user database.
  • You could designate a specific provider (e.g. Google) and only allow users with verified addresses at a specific domain to sign in.
  • You could use a Google Sheet, CSV or text file to whitelist users who can login.

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 goes into the JWT).

On that note, another thing I'm keen to add in 2.1 is JWT support, so that sessions don't need to be stored in a database.

That would be great yes!! ๐Ÿ™Œ

stumbled upon this today after seeing the incomplete features from the nextjs-auth0 integrations

thank you for all the hard work, given that @Fumler has already created the Prisma recipe, and Vercel has seemed to move forward with partnering with Prisma, maybe it can be merged to the project?

thank you for all the hard work, given that @Fumler has already created the Prisma recipe, and Vercel has seemed to move forward with partnering with Prisma, maybe it can be merged to the project?

@djstein I would love that! Given API changes I suspect it might have broken slightly, but I saved it in a ticket and am tracking that in this issue #151 I don't have any experience with Prisma and haven't tried to look at it myself yet.

As I'm currently testing this I figured that redirecting users to home page (could be customized) when they have already logged in.And prevent login routes when already logged in.

As I'm currently testing this I figured that redirecting users to home page (could be customized) when they have already logged in.And prevent login routes when already logged in.

Oh absolutely! This isn't actually being tracked in an issue, and I didn't mention it above, but has been discussed in some recent pull requests.

There will be additional functionality in the NextAuth.js client (similar to NextAuth v1), including signin() and signout() functions which you can just add to an onClick handler on a button.

They will take care of things like preserving the current page URL, so that the user is sent back to the right place after signing in or out - and so people don't have to interact with the default sign out page, which is really just intended as a fallback for clients without JavaScript.

I'm loving this project @iaincollins has done some outstanding work and really found a critical part of the nextjs stack we're missing. Thank you so much to @iaincollins and everyone in this thread for the long hours and hard work you're poured into this.

My side project involves Next.js, Discord, Paypal but critically https://fauna.com/ to act as a GraphQL server for not only the backend API calls inside Next.js but also the frontend. JWT looks like a match made in heaven as a simple lambda to manage this conversion would be ๐Ÿ‘Œ . I would imagine this would create a pretty nice future proof stack.

@eknowles Thanks! I would love you to follow up with that as an issue.

I think both Fauna and Prisma are great candidates for first class support in future.

JWT support is now released, but we will need to figure out how to update it to work for everyone and I would love your feedback on that specifically (it would be great as a new issue).

Last update to this thread:

Thank you everyone for your feedback!

We are very close to moving out of beta and releasing ~on time~ only slightly later than planned โ€“ with an amazing feature list for 2.0!

As this has grown quite long and things have changed during the beta, to make things easier to follow I'm closing this thread so I don't miss anything (and so that you don't all get spammed with old info) and and asking if you can move to the Release Candidate thread that just went up:

NextAuth 2.0 - Release Candidate

Thanks again for all your feedback and contributions! Please keep them coming.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

eatrocks picture eatrocks  ยท  3Comments

jimmiejackson414 picture jimmiejackson414  ยท  3Comments

ghoshnirmalya picture ghoshnirmalya  ยท  3Comments

simonbbyrne picture simonbbyrne  ยท  3Comments

Xetera picture Xetera  ยท  3Comments