Production grade Next.js

Auth with JWTs

Lock it down

Our app is coming together real nice! So far, the signed out experience is good to go, now we have to work on the app. Before we do that, we have to handle authentication. We need a few things:

  1. Create user accounts
  2. Sign a user in
  3. Protect pages to /app
  4. Lock down db accss

We're going to enlist the help of an amazing package called next-auth. It handles all of our auth needs and more. We want to allow users to signup with a Github account and then use a JWT to authenticate requests. Let's get started.

Setup

First thing is to conigure next-auth. We can do that by creating a catchall api route! ./pages/api/auth/[...nextauth] This is saying that we want any request to /api/auth/any/thing/after/auth to trigger this API function. Very nice!

./pages/api/auth/[...nextauth].ts
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'

export default (req, res) =>
  NextAuth(req, res, {
    session: {
        // use JWTs instead
      jwt: true,
    },
    jwt: {
      secret: process.env.JWT_SECRET,
    },
    providers: [
      Providers.GitHub({
        clientId: process.env.GITHUB_ID,
        clientSecret: process.env.GITHUB_SECRET,
      }),
      // ...add more providers here
    ],

    database: process.env.DATABASE_URL,
    pages: {
      signIn: '/signin',
    },
  })

This is the basic config for next-auth. With it, we get a few things:

  • github oauth
  • user creation
  • signin
  • JWT creation
  • auth page automatically configured with our providers (github) (we're using a custom one though)

To continue, you're going to need some secrets. Lets create a .env.local file on the root.

./.env.local
GITHUB_ID=
GITHUB_SECRET=
DATABASE_URL=
JWT_SECRET=
NEXT_PUBLIC_API_HOST=http://localhost:3000

First, you're you need to make a github oauth app. Call it whatever you'd like and add any URL for the home page url. For the Authorization callback url you can put http://localhost:1234. This will work no matter what PORT you're actullay running your app on. Grab your Client ID and generate a Client Secret and add them both to your env file respectively.

Next you need a database. If you have mongo >= 4 on your machinem you can start that and use it. If not, we'll get a hosted one. Head over to MongoDB Atlas and make an account. Create a new cluster, db user, and allow all IP addressess. Connect and get the DB URL. Add that url to the env file. Ensure you added that db user's username and password in the url, and change the dbname at the end to known

For a JWT_SECRET, you can put whatever you'd like. Then for NEXT_PUBLIC_API_HOST, just make sure you have the localhost url and the port is the same as the port you're running this app on. Next.js Defaults to 3000

We'r ready to sign some users up.

New account

Head over the signin page so we can get that working.

./pages/signin.tsx
// other imports
import { signIn, useSession } from 'next-auth/client'
import { useRouter } from 'next/router'

const Signin = () => {
  const [session, loading] = useSession()
  const router = useRouter()

 // if a user is logged it, never show them this page,
 // go straight to app
  useEffect(() => {
    if (session) {
      router.push('/app')
    }
  }, [session, router])
  
  // rest of component
}

We then need to setup and click handler for our github button

onClick={() => signIn('github')}

Now, when clicked this will trigger the github oauth flow and eventually create a user. Go ahead, try it!

Last thing we need to do is setup a provider so we can access the user session anywhere in the app.

./pages/_app.tsx
import { Provider } from 'next-auth/client'

function MyApp({ Component, pageProps }) {
  return (
  // auth provider
    <Provider session={pageProps.session}>
      <Component {...pageProps} />
    </Provider>
  )
}

This allows the useSession hook in the signinpage to actually work. Next we'll protect our pages.