I've been trying to implement a reliable authentication flow for a Next.js project but I'm completely lost now. I've already seen the examples repo of Next.js. But I have a lot of questions for a complete solution.
I have a express.js API and a separate Next.js frontend project. All the data and the authentication is handled by the API. Frontend just renders the pages with SSR. If I would just create a monolith project, where rendering the pages and all the data is handled by a single server (with a custom server option for Next.js I mean), I would just use express-session and csurf. It would be a traditional way to manage sessions and create security against CSRF.
Express.js API is not a requirement. It is just an example. It could be a Django API, or a .Net Core API. The main point is, it is a separate server and a separate project.
How can I have a simple, yet reliable structure? I've examined some of my favorite websites (netlify, zeit.co, heroku, spectrum.chat etc). Some of them use localstorage to store access and refresh tokens (XSS vulnerable). Some of them use cookies and they are not even HTTPOnly (both XSS and CSRF vulnerable). And examples like spectrum.chat use the way I mentioned above (cookie-session + preventing csrf).
I know there is the giant hype around the JWT tokens. But I find them too complex. Most of the tutorials just skips all the expiration, token refreshing, token revocation, blacklisting, whitelisting etc.
And many of the session cookie examples for Next.js almost never mention CSRF. Honestly, authentication is always a big problem for me. One day I read that HTTPOnly cookies should be used, next day I see a giant popular site not even using them. Or they say "never store your tokens to localStorage", and boom some giant project just uses this method.
Can anyone show me some direction for this situation?
We can authenticate a Nextjs app using the set of features it has. What we will do is that when a route in Nextjs is navigated to we will check the user to verify its credentials, if not successful we will redirect the user to a page, if it is successful we proceed to display the page.
Firebase configuration for authentication To integrate our Firebase project into our Next. js project, go to Project settings and find “SDK setup and configuration”, select radio Config. We will need to copy the snippet that has to be added to our project code.
Disclaimer: I am a maintainer of the free open source package below, but I think it's appropriate here as it's a common question there isn't a great answer for, as many of the popular solutions have the specific security flaws raised in the question (such as not using CSRF where appropriate and exposing Session Tokens or web tokens to client side JavaScript).
The package NextAuth.js attempts to address the issues raised above, with free open source software.
httpOnly
cookies with secure
.__HOST-
or __Secure
).e.g. page/api/auth/[...nextauth.js]
import NextAuth from 'next-auth' import Providers from 'next-auth/providers' const options = { providers: [ // OAuth authentication providers Providers.Apple({ clientId: process.env.APPLE_ID, clientSecret: process.env.APPLE_SECRET }), Providers.Google({ clientId: process.env.GOOGLE_ID, clientSecret: process.env.GOOGLE_SECRET }), // Sign in with email (passwordless) Providers.Email({ server: process.env.MAIL_SERVER, from: '<[email protected]>' }), ], // MySQL, Postgres or MongoDB database (or leave empty) database: process.env.DATABASE_URL } export default (req, res) => NextAuth(req, res, options)
e.g. pages/index.js
import React from 'react' import { useSession, signin, signout } from 'next-auth/client' export default () => { const [ session, loading ] = useSession() return <p> {!session && <> Not signed in <br/> <button onClick={signin}>Sign in</button> </>} {session && <> Signed in as {session.user.email} <br/> <button onClick={signout}>Sign out</button> </>} </p> }
Even if you don't choose to use it, you may find the code useful as a reference (e.g. how JSON Web Tokens are handled and how they are rotated in sessions.
I've had to think about this as well for my current project. I use the same technologies: an ExpressJS API and a NextJS server-side-rendered front-end.
What I chose to do is use passport.js in the ExpressJS API. TheNetNinja on YouTube has a really good playlist of this with 21 episodes. He shows you how to implement Google OAuth 2.0 in your API, but this logic transfers to any other strategy (JWT, Email + Password, Facebook authentication etc.).
In the front-end, I would literally redirect the user to a url in the Express API. This url would show the user the Google OAuth screen, the user clicks on "Allow", the API does some more stuff, makes a cookie for the specific user and then redirects back to a url in the front end. Now, the user is authenticated.
About HTTPOnly cookies: I chose to turn off this feature, because I was storing information in the cookie that I needed in the front-end. If you have this feature enabled, then the front-end (javascript) doesn't have access to those cookies, because they are HTTPOnly.
Here's the link to the playlist I was talking about: https://www.youtube.com/watch?v=sakQbeRjgwg&list=PL4cUxeGkcC9jdm7QX143aMLAqyM-jTZ2x
Hope I've given you a direction you can take.
EDIT: I haven't answered your question about CSURF, but that's because I'm not familiar with it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With