Next.js: Add login / authentication example

Created on 29 Oct 2016  ·  208Comments  ·  Source: vercel/next.js

With:

  • re-usable authentication helper across pages
  • session synchronization among tabs
  • simple passwordless email backend hosted on now.sh

I think this will be hugely helpful to a lot of newcomers.

p0

Most helpful comment

So I have auth working swimmingly. As mentioned elsewhere, it's client-side only, which is ultimately just half the battle.

"Pretty-secure"

Like php, the atomic unit of Next is the page. One of the coolest features is that it lazy loads each page only when it's requested. With client-side only auth but with server-rendering, the js for that protected page is in fact downloaded by the browser. In the future when Next adds server workflows, you'll hopefully be able to block render and redirect on the server to prevent this entirely. This will require cookies, sessions, and AFAIK session stores, but that's just the cost of doing hybrid apps like these.

Auth Example

Assume you have a JWT-secured API with two endpoints of interest: /token and /me. /token accepts email/password credentials and returns a signed JWT (id_token) while /me returns profile information related to the JWT-authenticated user. I adapted the following AuthService.js from Auth0's lock (removing event emitter, although that's not the worst idea). It extracts almost all of the JWT token handling so it can be used on the login page and also in a Higher Order Component (more on that later).

// utils/AuthService.js
export default class AuthService {
  constructor(domain) {
    this.domain = domain || 'http://localhost:5000'
    this.fetch = this.fetch.bind(this)
    this.login = this.login.bind(this)
    this.getProfile = this.getProfile.bind(this)
  }

  login(email, password) {
    // Get a token
    return this.fetch(`${this.domain}/token`, {
      method: 'POST',
      body: JSON.stringify({
        email,
        password
      })
    }).then(res => {
      this.setToken(res.id_token)
      return this.fetch(`${this.domain}/user`, {
        method: 'GET'
      })
    }).then(res => {
      this.setProfile(res)
      return Promise.resolve(res)
    })
  }

  loggedIn(){
    // Checks if there is a saved token and it's still valid
    const token = this.getToken()
    return !!token && !isTokenExpired(token) // handwaiving here
  }

  setProfile(profile){
    // Saves profile data to localStorage
    localStorage.setItem('profile', JSON.stringify(profile))
  }

  getProfile(){
    // Retrieves the profile data from localStorage
    const profile = localStorage.getItem('profile')
    return profile ? JSON.parse(localStorage.profile) : {}
  }

  setToken(idToken){
    // Saves user token to localStorage
    localStorage.setItem('id_token', idToken)
  }

  getToken(){
    // Retrieves the user token from localStorage
    return localStorage.getItem('id_token')
  }

  logout(){
    // Clear user token and profile data from localStorage
    localStorage.removeItem('id_token');
    localStorage.removeItem('profile');
  }

  _checkStatus(response) {
    // raises an error in case response status is not a success
    if (response.status >= 200 && response.status < 300) {
      return response
    } else {
      var error = new Error(response.statusText)
      error.response = response
      throw error
    }
  }

  fetch(url, options){
    // performs api calls sending the required authentication headers
    const headers = {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    }

    if (this.loggedIn()){
      headers['Authorization'] = 'Bearer ' + this.getToken()
    }

    return fetch(url, {
      headers,
      ...options
    })
    .then(this._checkStatus)
    .then(response => response.json())
  }
}

Next up is a HOC to make protecting pages simpler. To prevent an unwanted flash of sensitive info, the page will server-render Loading... on first render while react boots up / reads the token from localStorage. This means that protected pages will not SEO, which is probably okay as of now, but definitely not optimal.

// utils/withAuth.js - a HOC for protected pages
import React, {Component} from 'react'
import AuthService from './auth'

export default function withAuth(AuthComponent) {
    const Auth = new AuthService('http://localhost:5000')
    return class Authenticated extends Component {
      constructor(props) {
        super(props)
        this.state = {
          isLoading: true
        };
      }

      componentDidMount () {
        if (!Auth.loggedIn()) {
          this.props.url.replaceTo('/')
        }
        this.setState({ isLoading: false })
      }

      render() {
        return (
          <div>
          {this.state.isLoading ? (
              <div>LOADING....</div>
            ) : (
              <AuthComponent {...this.props}  auth={Auth} />
            )}
          </div>
        )
      }
    }
}
// ./pages/dashboard.js
// example of a protected page
import React from 'react'
import withAuth from  '../utils/withAuth'

class Dashboard extends Component {
   render() {
     const user = this.props.auth.getProfile()
     return (   
         <div>Current user: {user.email}</div>
     )
   }
}

export default withAuth(Dashboard) 

The login page can't use the HOC as it stands now, because Login needs be public. So it just makes an instance of AuthService directly. You would do something similar for a Signup page too.

// ./pages/login.js
import React, {Component} from 'react'
import AuthService from '../utils/AuthService'

const auth = new AuthService('http://localhost:5000')

class Login extends Component {
  constructor(props) {
    super(props)
    this.handleSubmit = this.handleSubmit.bind(this)
  }

  componentDidMount () {
    if (auth.loggedIn()) {
      this.props.url.replaceTo('/admin')   // redirect if you're already logged in
    }
  }

  handleSubmit (e) {
    e.preventDefault()
    // yay uncontrolled forms!
    auth.login(this.refs.email.value, this.refs.password.value)
      .then(res => {
        console.log(res)
        this.props.url.replaceTo('/admin')
      })
      .catch(e => console.log(e))  // you would show/hide error messages with component state here 
  }

  render () {
    return (
      <div>
         Login
          <form onSubmit={this.handleSubmit} >
            <input type="text" ref="email"/>
            <input type="password" ref="password"/>
            <input type="submit" value="Submit"/>
          </form>
      </div>
    )
  }
}

export default Login

Inspired by Airbnb's react-with-styles, I also started working on a next-with-auth lib which would be a function returns a HOC to be used on pages. I also played with merging AuthService and this HOC. One solution might be to make this HOC accept a permission level function as an argument in addition to the component, like redux connect. Regardless, in my mind, you would use next-with-auth like this:

// ./utils/withAuth.js
import nextAuth from 'next/auth'
import parseScopes from './parseScopes'

const Loading = () => <div>Loading...</div>

export default nextAuth({
  url: 'http://localhost:5000',
  tokenEndpoint: '/api/token',
  profileEndpoint: '/api/me',
  getTokenFromResponse: (res) => res.id_token,
  getProfileFromResponse: (res) => res,
  parseScopes,
})

Doing this all with Redux seemed unnecessarily complicated, but basically you can follow the wiki example, but move AuthService into Actions (login and logout) and have a User Reducer. You could only call these actions on the client though, since there isn't localStorage on the server, so you need to check for that in your Actions. Ultimately, redux store is put on the window anyways. So you could just a well cache the user on window on your own instead of using context. If you don't want redux, you can also try out react-broadcast.

Lastly, assuming next/server ships according to #25. next-with-auth could abstract complicated localStorage vs. cookie stuff away from the developer with middleware + a HOC. It could also handle token refreshing too.

All 208 comments

Suggestion: Use Redux and JWT to accomplish the example

Im working on an example for this. Currently having issues getting componentWillReceiveProps to fire on my high level component (where Im planning to check if user is authenticated and redirect to login page if not)

So I have auth working swimmingly. As mentioned elsewhere, it's client-side only, which is ultimately just half the battle.

"Pretty-secure"

Like php, the atomic unit of Next is the page. One of the coolest features is that it lazy loads each page only when it's requested. With client-side only auth but with server-rendering, the js for that protected page is in fact downloaded by the browser. In the future when Next adds server workflows, you'll hopefully be able to block render and redirect on the server to prevent this entirely. This will require cookies, sessions, and AFAIK session stores, but that's just the cost of doing hybrid apps like these.

Auth Example

Assume you have a JWT-secured API with two endpoints of interest: /token and /me. /token accepts email/password credentials and returns a signed JWT (id_token) while /me returns profile information related to the JWT-authenticated user. I adapted the following AuthService.js from Auth0's lock (removing event emitter, although that's not the worst idea). It extracts almost all of the JWT token handling so it can be used on the login page and also in a Higher Order Component (more on that later).

// utils/AuthService.js
export default class AuthService {
  constructor(domain) {
    this.domain = domain || 'http://localhost:5000'
    this.fetch = this.fetch.bind(this)
    this.login = this.login.bind(this)
    this.getProfile = this.getProfile.bind(this)
  }

  login(email, password) {
    // Get a token
    return this.fetch(`${this.domain}/token`, {
      method: 'POST',
      body: JSON.stringify({
        email,
        password
      })
    }).then(res => {
      this.setToken(res.id_token)
      return this.fetch(`${this.domain}/user`, {
        method: 'GET'
      })
    }).then(res => {
      this.setProfile(res)
      return Promise.resolve(res)
    })
  }

  loggedIn(){
    // Checks if there is a saved token and it's still valid
    const token = this.getToken()
    return !!token && !isTokenExpired(token) // handwaiving here
  }

  setProfile(profile){
    // Saves profile data to localStorage
    localStorage.setItem('profile', JSON.stringify(profile))
  }

  getProfile(){
    // Retrieves the profile data from localStorage
    const profile = localStorage.getItem('profile')
    return profile ? JSON.parse(localStorage.profile) : {}
  }

  setToken(idToken){
    // Saves user token to localStorage
    localStorage.setItem('id_token', idToken)
  }

  getToken(){
    // Retrieves the user token from localStorage
    return localStorage.getItem('id_token')
  }

  logout(){
    // Clear user token and profile data from localStorage
    localStorage.removeItem('id_token');
    localStorage.removeItem('profile');
  }

  _checkStatus(response) {
    // raises an error in case response status is not a success
    if (response.status >= 200 && response.status < 300) {
      return response
    } else {
      var error = new Error(response.statusText)
      error.response = response
      throw error
    }
  }

  fetch(url, options){
    // performs api calls sending the required authentication headers
    const headers = {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    }

    if (this.loggedIn()){
      headers['Authorization'] = 'Bearer ' + this.getToken()
    }

    return fetch(url, {
      headers,
      ...options
    })
    .then(this._checkStatus)
    .then(response => response.json())
  }
}

Next up is a HOC to make protecting pages simpler. To prevent an unwanted flash of sensitive info, the page will server-render Loading... on first render while react boots up / reads the token from localStorage. This means that protected pages will not SEO, which is probably okay as of now, but definitely not optimal.

// utils/withAuth.js - a HOC for protected pages
import React, {Component} from 'react'
import AuthService from './auth'

export default function withAuth(AuthComponent) {
    const Auth = new AuthService('http://localhost:5000')
    return class Authenticated extends Component {
      constructor(props) {
        super(props)
        this.state = {
          isLoading: true
        };
      }

      componentDidMount () {
        if (!Auth.loggedIn()) {
          this.props.url.replaceTo('/')
        }
        this.setState({ isLoading: false })
      }

      render() {
        return (
          <div>
          {this.state.isLoading ? (
              <div>LOADING....</div>
            ) : (
              <AuthComponent {...this.props}  auth={Auth} />
            )}
          </div>
        )
      }
    }
}
// ./pages/dashboard.js
// example of a protected page
import React from 'react'
import withAuth from  '../utils/withAuth'

class Dashboard extends Component {
   render() {
     const user = this.props.auth.getProfile()
     return (   
         <div>Current user: {user.email}</div>
     )
   }
}

export default withAuth(Dashboard) 

The login page can't use the HOC as it stands now, because Login needs be public. So it just makes an instance of AuthService directly. You would do something similar for a Signup page too.

// ./pages/login.js
import React, {Component} from 'react'
import AuthService from '../utils/AuthService'

const auth = new AuthService('http://localhost:5000')

class Login extends Component {
  constructor(props) {
    super(props)
    this.handleSubmit = this.handleSubmit.bind(this)
  }

  componentDidMount () {
    if (auth.loggedIn()) {
      this.props.url.replaceTo('/admin')   // redirect if you're already logged in
    }
  }

  handleSubmit (e) {
    e.preventDefault()
    // yay uncontrolled forms!
    auth.login(this.refs.email.value, this.refs.password.value)
      .then(res => {
        console.log(res)
        this.props.url.replaceTo('/admin')
      })
      .catch(e => console.log(e))  // you would show/hide error messages with component state here 
  }

  render () {
    return (
      <div>
         Login
          <form onSubmit={this.handleSubmit} >
            <input type="text" ref="email"/>
            <input type="password" ref="password"/>
            <input type="submit" value="Submit"/>
          </form>
      </div>
    )
  }
}

export default Login

Inspired by Airbnb's react-with-styles, I also started working on a next-with-auth lib which would be a function returns a HOC to be used on pages. I also played with merging AuthService and this HOC. One solution might be to make this HOC accept a permission level function as an argument in addition to the component, like redux connect. Regardless, in my mind, you would use next-with-auth like this:

// ./utils/withAuth.js
import nextAuth from 'next/auth'
import parseScopes from './parseScopes'

const Loading = () => <div>Loading...</div>

export default nextAuth({
  url: 'http://localhost:5000',
  tokenEndpoint: '/api/token',
  profileEndpoint: '/api/me',
  getTokenFromResponse: (res) => res.id_token,
  getProfileFromResponse: (res) => res,
  parseScopes,
})

Doing this all with Redux seemed unnecessarily complicated, but basically you can follow the wiki example, but move AuthService into Actions (login and logout) and have a User Reducer. You could only call these actions on the client though, since there isn't localStorage on the server, so you need to check for that in your Actions. Ultimately, redux store is put on the window anyways. So you could just a well cache the user on window on your own instead of using context. If you don't want redux, you can also try out react-broadcast.

Lastly, assuming next/server ships according to #25. next-with-auth could abstract complicated localStorage vs. cookie stuff away from the developer with middleware + a HOC. It could also handle token refreshing too.

Excited to try this out! Thanks for the barebones implementation :)

@jaredpalmer I'm working on something similar. How does your AuthService work when a component is rendered server side? The server would need access to the JWT but can't read it from local storage.

@amccloud It doesn't. That's the whole issue. The HOC renders <div>Loading..</div> on protected routes and must read the token and decide whether or not to redirect in componentDidMount. For it to work the way you want it to and render server-side, Next needs #25, or at least the ability to set a cookie with the value of the JWT AFAIK.

I used cookie-js to set a cookie, but it's a bit of a hack..
the thing is: if you don't send a cookie, you lose all the benefits of nextjs and server side rendering in authenticated routes

@jaredpalmer this is great! thanks for the effort. I'll try to finish implementing your example (or help you doing it if you want) in the following days

Yo! I published an example with nextjs and auth0 here: https://github.com/luisrudge/next.js-auth0
It has the concept of a main layout and also "secure pages" that load only when the user is authenticated.
Let me know what you think 🎉

@luisrudge amazing. I'm cloning and doing some changes but looks great

Cool! What do you think it's missing? What changes are you thinking?

On Sun, Nov 6, 2016 at 1:12 PM -0200, "Dan Zajdband" <[email protected]notifications@github.com> wrote:

@luisrudgehttps://github.com/luisrudge amazing. I'm cloning and doing some changes but looks great

You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHubhttps://github.com/zeit/next.js/issues/153#issuecomment-258687108, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AA5cE8NIsvQ_ITjc1gArTFgNXzEda4TSks5q7e5NgaJpZM4KkJmi.

1) Using standard for linting (so it's consistent with everything we are building with next)
2) Adding multi-tab support requested by @rauchg
3) The css part can be simplified

I'll send you a pr :)

What do you mean by multi tab support?

On Sun, Nov 6, 2016 at 1:16 PM -0200, "Dan Zajdband" <[email protected]notifications@github.com> wrote:

1) Using standard for linting (so it's consistent with everything we are building with next)
2) Adding multi-tab support requested by @rauchghttps://github.com/rauchg
3) The css part can be simplified

I'll send you a pr :)

You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHubhttps://github.com/zeit/next.js/issues/153#issuecomment-258687373, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AA5cE1A6jq4KZc9_ynukTCI4mU-rdsNaks5q7e81gaJpZM4KkJmi.

You have 2 open tabs, logout on 1, automatically logs out on the other

Ahh. That's super cool!

On Sun, Nov 6, 2016 at 1:21 PM -0200, "Dan Zajdband" <[email protected]notifications@github.com> wrote:

You have 2 open tabs, logout on 1, automatically logs out on the others

You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHubhttps://github.com/zeit/next.js/issues/153#issuecomment-258687707, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AA5cE9e2DA4_GgNQIVTMp0hx74G-6RmUks5q7fBfgaJpZM4KkJmi.

Hi @luisrudge I sent you a PR with the changes https://github.com/luisrudge/next.js-auth0/pull/2

thank you so much for doing this <3

btw this is the result:

2016-11-06 11 14 31

@impronunciable @luisrudge Fantastic implementation! If you want to use it without Auth0, it looks you'd only need to change the files in the ./utils dir, maybe even just lock.js. I'll be trying this out soon. Btw the multi-tab looks awesome 💯

@ugiacoman I've started implementing a small server with passwordless.net, let me know if you want to get my code as a starting point

@impronunciable That'd be awesome! I was actually going to do something similar with Twitter Fabric's Digits.

@impronuncible i suggest not using password less.net , instead you can just use passport-local, and just send users a link with their email and token in query string.

Thanks @impronunciable ❤️

@ugiacoman yeah, it's pretty easy to remove the auth0 dependency. I used it because I didn't want to have a separate api to handle auth

@jaredpalmer as far as I know, having #25 would be great but isn't blocking? I mean we have access to the server-side req in getInitialProps so nothing prevents applying cookie-parser to it? Server-side auth and session management is all new stuff to me 😬

BTW considering localStorage can't be used server-side, are cookies the only way to have server-side sessions? I have a vague remembrance it might not be the safest? But is there any other option?

@sedubois

The cookie approach can be very safe if done properly. Doing the following is fairly trivial:

  • use httpOnly flag (prevents JavaScript access to cookie)
  • use secure flag (only set cookie for https requests)
  • Signed cookies (verify source of cookie)

There's also a very significant latency advantage when you can access authentication information directly on the server.

We should move this example into examples/ I'll see what I can come up with

I've manage to use react-cookie for isomorphic cookies by wrapping nextjs in a custom express server the following way:

const express = require('express')
const next = require('next')
const cookie = require('react-cookie')
const cookieParser = require('cookie-parser')

const app = next({ dev: true, dir: process.cwd() })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  const server = express()
  server.use(cookieParser())       // <---- this line

  server.get('*', (req, res) => {
    cookie.plugToRequest(req, res) // <---- this line
    return handle(req, res)
  })

  server.listen(3000, (err) => {
    if (err) throw err
    console.log('> Ready on http://localhost:3000')
  })
})

This allows me to make authenticated request from the server side. This does not address any of the issues original bullets but it solved the problem of sharing state between client and server.

From the POV of someone who is learning a lot of this stuff. I think it would be best if examples aren't relying on 3rd party services like auth0. It would be more beneficial for newcomers to see a more barebones example with login/signup forms and using Redux and JWT.

The example we plan to _bundle_ will be based on open-source Node.js server APIs

I've added an example of email based authentication to an example starter project at https://github.com/iaincollins/nextjs-starter

It has session support (with Express Sessions on the backend and the browser sessionStorage API to cache them on the front end), httpOnly cookies, CSRF projection, uses built in SMTP to send emails, an easy to change the backend that defaults to SQL Lite. No configuration is required to run it.

The project also has layout pages, custom routes and includes the clock example from the wiki. It's not the fanciest example of authentication but might be helpful for those looking for easy to get started with simple project that is easy to understand and play around with.

I agree with @iamjacks that an example with JWT seems like a good idea.

I am happy to improve error handling and add features like a simple profile page users can edit, and passport integration with examples for oAuth for Facebook, Google and Twitter if that would be useful for people. If folks have good ideas about better ways to propagate / expose session info to components I'm super interested.

Example screenshot showing what to expect

That's pretty incredible @iaincollins. We'll feature this on the release notes for 2.0 for sure :)

@rauchg Thank you! :)

I guess I should add explicitly that in this example sessions are both client and server based - i.e. work with and without JavaScript (and on systems without sessionStorage), and the same session is shared across both.

Achieving this gets a little gnarly in the Session component, which delves into variables with names like req.connection._httpMessage.locals._csrf to get the CSRF token from the server headers - as the 'req' object passed to pages in getInitialProps() is curiously bit different from the req object exposed in Express as I'd normally access it through req.locals._csrf (however req.session is the same in both).

localStorage is not secure. what is the best way to make it more secure. anyone can steal localStorage data and put it again in his browser and can be logged as the victim user!!

@Chathula that's not true. This argument is wrong.
If anyone has access to the browser physically, they can do anything.

That's true for cookies as well.

Using localStorage for auth is kind a secure because we might get rid of cookie based sec issues.
But on the otherhand, it affects SSR.

@arunoda i have created login with Laravel API and Next.js Client. i store authUser access_token inside localStorage. then check user logged or not by authenticating. but it is not secure. if someone stole the localStorage data. he/she can use it.

if someone stole the localStorage data.

How? Basically he/she should have access to the browser physically. Then that person could do anything.
So, we should not worry about that.

can't we use any encryption to make it much more secure?

@Chathula this is going off topic. It's not exactly relevant to Next.js and we want to go with how normally web stuff works.

@Chathula may be you could start a new thread in the above starter-project.

@arunoda hahha!! Thank you for info! :D

@Chathula I'm happy discuss it more in an issue the starter project if you have specific concerns.

I'd like to correct any misapprehension, to avoid people being unnecessarily alarmed.

The Web Storage API (i.e. localStorage and sessionStorage) is - like cookies without httpOnly set - restricted via same origin policy (protocol, hostname, port number), it is not true that "anyone can steal [it]"; but yes if someone is able to execute arbitrary JavaScript on your site via a Cross Site Scripting Vulnerability in your application then they can access the store too, so you should not store session identifiers in it.

This is why you will see that session token itself is not stored in localStorage/sessionStorage and is not readable in JavaScript, it is only transmitted via an HTTP Only cookie (which is why the session class uses XMLHttpRequest() rather than fetch() - as explained in the class documentation).

This means even if someone is able to exploit a Cross Site Scripting Vulnerability in your application and execute arbitrary JavaScript in your web app they still cannot read or export a users session token.

This is perhaps an important distinction worth labouring in the documentation.

Note: Additional encryption of user data isn't helpful here because the app always needs to be able to read the data so it can be rendered (so you'd also need to store a description key in the app, which would render encrypting the user data fairly moot).

_UPDATE: In the last week or two the example was refactored to use localStorage over sessionStorage as sessionStorage is not shared between tabs and sharing non-senstive data this way reduces the number of unnecessary auth checks and keeps session status consistent between tabs._

Maybe useful to some people I made this sample app while experimenting:

https://github.com/possibilities/next.js-with-auth

Backed by this toy backend:

https://github.com/possibilities/micro-auth

Deployed here:

https://next-with-auth.now.sh/

Backend here:

https://micro-auth.now.sh/

@possibilities Thanks Mike! As well as exposing a separate microservice that shows a nice way of handling secure pages that I was thinking might be good pragma and might take inspiration from. I have some ideas which will will raise in the next.js-with-auth repo.

After giving some more thought I'd probably not hold my efforts above as a great example. I would likely change to having the signup/signin fully submit web 1.0 style so that we can establish an HTTP only cookie on the server (eliminating accessibility to the JWT via XSS) and then attach the user object to req rather than the entire token. This leaves the possibility of CSRF vulnerabilities but I think this is more straightforward to mitigate than XSS (no?). A nice side effect of this is then the client app can use a nearly identical flow for when signing in via an oath service.

I also see in hindsight I could avoid the "middleware" (and therefore need for custom server) by parsing the cookie in the Page HoC's getInitialProps.

@possibilities Since the 'secret' page is just a page and is bundled by webpack, wouldn't it be served to the browser if i go to /secret?

Yeah, I suppose that should be wired to do a server-side redirect.

BTW, I got distracted back to my initial project of signing in with github. The flow is similar with more care about security (namely not exposing any secret on the client to avoid oauth token being exposed via XSS). It's tied up in a proper app but if there's any interest I could break it out into something that could be useful for oauth flow in general.

@possibilities I think it would be an awesome help, if there could be a PR with a most minimal (but proper) with-auth example 🙂 I'm working on auth in my app (https://github.com/relatenow/relate) but it's currently only client-side (localStorage).

@sedubois I have a pr for that using passwordless.net https://github.com/zeit/next.js/pull/646 but we'll move the auth server somewhere else

what about using graphql?

Apollo gives an example of graphql authentication:

https://dev-blog.apollodata.com/a-guide-to-authentication-in-graphql-e002a4039d1

Here we are authenticating a graphql request, but it could be adapted for our case.

Plus graphql can abstract away implementation and logic. It could be used with passwordless, auth0, or anything else we might prefer.

@impronunciable FYI with your example I don't yet know how to handle sessions, as I want to get rid of passwordless. I'll try adapting @iaincollins' example in my app.

My requirements are:

  • secure
  • authenticating server side then client side
  • support both Facebook and login/password
  • auth providers should be easily changed
  • create user in Graphcool
  • authenticate subsequent GraphQL requests to Graphcool

In case it helps someone here's my app with server-side auth 🙂

The auth should be split out from Next.js server but I'm waiting for someone else to give inspiration about that... Also I'm not sure if it's properly protected against CSRF.

This is what i did:

  • when the user logs in, client-side javascript sets a cookie in the browser conintaing the authentication bearer token
  • when making authenticated requests server-side (next.js) code reads the bearer token in the headers of the request of the browser and uses it to contact the api server on behalf of the client

This is vulnerable to XSS (even if react does a lot to prevent it) and CSRF attacks but it is simple and works with SSR.

Just to add to the requests

graph.cool + apollo + jwt + auth0 + next.js, the first 4 parts of that are already done at https://github.com/graphcool-examples/react-apollo-auth0-example

@balupton in your example what happens when the token expires while the user is still connected, is the session just over or is it renewed somehow?

@nmaro don't know, not written by me - best to ask over there

For my own app, I've got auth going with next.js and auth0, then with a zeit/micro API server that verifies the bearer tokens.

Should be able to open-source sometime in February.

Following the same philosophy that next.js has been following (do one thing and do it well), I've developed the skeleton of an extensible user accounts system for node. See the philosophy here: https://medium.com/the-ideal-system/ooth-user-accounts-for-node-js-93cfcd28ed1a#.97kyfg4xg

The github project is here: https://github.com/nmaro/ooth/

The goal is to have an extensible, independent authentication + user management service that's ideal to be used as a separate microservice, an application structure that would play very well with node.js.

An example with email+password authentication is here: https://github.com/nmaro/ooth/tree/master/examples/ooth
There are already packages in place for Facebook and Google auth (ooth-facebook, and ooth-google), which should be easy to implement based on passport-facebook and passport-google respectively.

I'll be posting a next.js integration example asap. Feel free to join the discussion and contribute.

Sorry for the plug, but it's for the greater good - I really believe there is no good solution for node yet, and this is just the right audience of people who might want such a thing. Peace

Meanwhile... here is an example graphql API that requires authentication with a JWT-Token just for write operations. Feel free to use it with your favorite authentication method :)

https://github.com/nmaro/ooth/tree/master/examples/graphql-api-with-auth

I've updated https://nextjs-starter.now.sh to add oAuth support.

screen shot 2017-02-10 at 05 03 19

  • It uses Passport for oAuth, together with express-sessions (as before).
  • There is support for Facebook, Google and Twitter+ oAuth and it's easy to add more (see AUTHENTICATION.md and routes/auth-passport.js).
  • It uses the universal client/server session system (with CSRF tokens, XSS protection via HTTP Only cookies, the ORM layer that supports Mongo, SQL DBs, Redshift, etc.) as the email sign in uses.
  • Can report the experience of configuring oAuth on developer portals is just as terrible as ever (weird errors happen and it's hard to debug).

The nature of oAuth - db+sessions+passport and error handling needing to work closely together - and of sessions needing to work both client and server and how that works with universal rendering - means a little bit much to try and wrap around at once if you are trying to figure out what is going on, but the client logic doesn't have any oAuth specific configuration so it's not too messy.

I'd be happy to split off just authentication into a separate example, although I suspect it wouldn't be a lot smaller. If someone else wants to, great. I'll probably add a little more to the example at some point (like an account management page). Probably linking more to the documentation would be good.

Amazing work! If you could split the auth And add it to the next.js repo that would be awesome :heart:

I can do it too btw 👍🏻

I won't have time over the next 2 weeks or so, so if someone wants do that would be great.

I'd love to refactor it and see if it could be reduced to just a module (that exposes simple components like login buttons and sign in forms to embed), and to take some inspiration from the really nice earlier client-side-only example by @impronunciable.

Update: I'm actually going away, which is why I can't, but when I'm back I'm happy to look at doing that!

I've followed the auth0/react quick start guide with some modification, but when I call lock.show(), the app complaints:

Uncaught Error: addComponentAsRefTo(...): Only a ReactOwner can have refs. You might be adding a ref to a component that was not created inside a component's render method, or you have multiple copies of React loaded

@iaincollins @timneutkens regarding your example, please correct me if I am wrong.

The example uses an httpOnly cookie to store a session token making it safe against javascript injiection attacks (XSS). Then it goes into the trouble of having a csrf token stored in local storage to also protect against CSRF attacks.

There is an underlying assumption that this combination of techniques makes things safe, which could mislead the user/developer. An attacker can still injiect javascript in the page (XSS), read the csrf token, and use it to perform authenticated (cookie) requests to the apis. Is worth mentioning in the readme ?

Hi @davibe

Alas, there isn't currently a technically better approach than a session cookie and a separate rotating CSRF token, so guess I have to say I'm broadly happy with the model as is it as there isn't really another way to do it (even if the actual implementation could always be improved).

We could add browser finger printing, maybe the session token could rotate more frequently (I forget if that is automatic right now), the CSRF token could be a header instead of param, the cookies should really be SSL only in production and we could add an expiry date to the sign in tokens, but AFAICS there's fairly limited scope for improvement as far as the security model goes and and nothing that really grant further protection in the event someone is able to inject any code they like into to an app; but please do feel free to raise those things as issues for improvements in the repo.

If there were any account status modifying options (which there aren't currently) we could have a CAPTCHA before performing them to make it more difficult to make changes on the server without permission, but all that happens in the example is users can sign in and out so that's currently out of scope.

Storing session tokens local storage would make it notably less secure and go against the intended use in the spec, so personally I'm not in favour of that - though I appreciate it would simplify things, as does not having CSRF projection, but people probably don't need an example of that as it would be very simple to do - I've only tried to provide one because it is so awkward. :-)

I am fully with you in that I too hate how necessarily awkward it is and have not given up on trying to turn it into a module.

Hmm.. I understand.
This issue was very useful to me, thank you.

This is the point of view of Auth0 https://auth0.com/blog/cookies-vs-tokens-definitive-guide/
They basically suggest to avoid cookies but that would leave out SSR on first page load i think.

Hey everyone!

I'm also working on authentication with next.js and the comments from this issue, in particular @davibe + the repo from @iaincollins and the PR from @timneutkens have been of tremendous help.

My solution does the following:

  • Upon successful login, my redux reducer saves the token and user data in a cookie using react-cookie.
  • Any page / component that needs that information is wrapped in a higher-order component similar to @timneutkens with-session.js. If SSR, the token will be available in ctx.req.headers.cookie, otherwise just grab it from the browser document (use react-cookie load method).
  • Once I have the token, I can set it in the Bearer / Authorization headers whenever I make a request.

It helps that I have my own JWT token authentication microservice running in a docker container.
Perhaps it would be easier to provide a simple JWT service as part of the with-auth example? Thus avoiding to hack the server.js and potentially losing the benefits of next.js baked-in hot reloading, ssr and routing?

I'm also not sure whether this approach is safe in terms of CSRF / XSS. Any comments are welcome.

Thanks to you all for the amazing work you've done so far. I'm a big fan of this project!

@jcsmesquita I'm working on a new version of the example with everything separated from Next.js similar to how auth is implemented at zeit.co.

@subsumo Thanks for mention, FYI : NAP is mine, the authen via web is base on @iaincollins nextjs-starter plus react native client login with token.

@timneutkens does the solution you are working on allow you to authenticate towards a separate service?

I've been looking at the current auth example pull request https://github.com/zeit/next.js/pull/1141
It seems to me that it only allows to authenticate towards the next.js server, but not isomorphically towards a separate service.

In other words, assume you want to separate the next.js server and the actual app API (e.g. REST or GraphQL), then what you want to do is that the client and the server are able to authenticate towards the API. I don't think this solution really helps you there.

I thought of a flow.

Entities:

  • Client (C)
  • Next.js Server (S)
  • API (A)

The goal is to establish 3 cookie-based sessions:

  1. C-S
  2. C-A
  3. S-A

Session 1) is so the Client recognizes the Server, and 2) 3) are so that both Client and Server can use their respective sessions to access the API independently.

This is the flow:

  1. C-S is easily done, no authentication needed
  2. C-A is done with authentication (e.g. with username/password). Additionally, a JWT is provided
  3. Client provides JWT to Server and then discards it
  4. S-A is done with JWT, which is then discarded

What's your opinion? Is there an easier way? Should we instead just keep the API coupled with the next.js server, so that only one session is needed (C-S)?

@rauchg I don't summon you easily, but I believe this is also about what direction next.js has to go - as a "universal front-end" should it run separately from the API providing the data? If yes we need to get this right.

@timneutkens does the solution you are working on allow you to authenticate towards a separate service?

That's the idea yeah. Been busy fixing other stuff on Next.js. Will get back to it ASAP.

I broke out github-auth-specific parts of my app into a pretty digestible example. might be interesting for some and would love any feedback on the code or flow: https://github.com/possibilities/next-github-auth-example

UPDATE: I refactored the example app into a reusable set of components that can be "dropped into" next apps

Follow-up: I wrote an integration with ooth and a GraphQL API.

https://medium.com/the-ideal-system/ooth-user-accounts-for-node-js-93cfcd28ed1a#.ykoj1dhil

it's based on existing approaches, i.e. authentication towards the next.js server, that is it assumes the api and the authentication server to run all in the same process, so only one session has to be created. The advantage: in principle it can store credentials for / is extensible to virtually any passport.js strategy.

@timneutkens any progress on that front?

I refactored my github auth example app into a reusable set of decorators and page component for “dropping github auth into” next apps. Code and functionality feedback welcomed. https://github.com/possibilities/next-github-auth

I _think_ it could be interesting to nail down the boards here and then continue evolving this into a more generic auth framework for next.

@timneutkens
Sorry to ping you but have you made any progress on this? I am completely lost on how should I set up authentication with next.js properly.

@kolpav I have done some work on it, currently swamped with other stuff 😥 By all means this is pretty high on my priorities list for Next 😄

A clear, well documented and secure way of achieving authentication with a next.js app is critical to it's success as a framework.

I can't remember the last time I built a web that did not have authentication.
Like most people I imagine, I also want to make secured calls to my backed for rest data, so JWT seems the obvious solution.

But there is so much discussion among the issues and PR's I'm not sure where to start!

@timneutkens
Cool 👍 I think it is gonna be very valuable for others.

@camstuart @kolpav There are some good, working examples above including supporting oAuth and email based auth that uses both JWT and HTTP Cookies by contributors @jaredpalmer, @luisrudge, @impronunciable, @possibilities and myself.

To highlight some links work checking out:

(The micro auth example was good but I think it needs updating.)

There is scope for further improvement, which additional commentators have commented on above - including a session store component and splitting the server logic; and an example that simplifies things even further which Tim has been working on.

Simplicity for auth is a challenging area for Universal apps, but you should be able to get the above examples going - or just try out the demos directly - and see how they work without too much fuss.

@iaincollins That's great examples. But how can I use (for example) starter project? So If I want to build my app. I need to clone this repo? Or I need to "copy-paste" code from starter project chunk by chunk to my own code?

If starter project will be updated - what I should do?

@iaincollins
Nice examples especially yours.
But still, I would like to see auth example with zeit seal of approval on it 😄 all eyes would point into one direction so any errors or mistakes would not go unnoticed. For now, I have my own working auth but I am not sure how secure it is.

Agreed @kolpav, rolling your own security is a tricky business. Best left to the experts

I have created a stack for this that can handle authentication easily with GraphQL: https://github.com/thebillkidy/MERGE-Stack

@salmazov I've found a useful way to get started and understand what's going on in an example or starter project can be to fork it, then strip out things that aren't relevant until you are left with just the code related to the functionality you want to implement; and then to try porting just that functionality over to over to another project.

@kolpav @camstuart This thread contains really extensive discussion of various security models, including debunking some common misconceptions and trade-offs. I'd particularly note the points about HTTP Only Cookies and CSRF tokens (and the additional protection they give against XSS and CSRF over using JWT and/or the Web Storage API for session tokens). It's really worth a read.

@iaincollins Did you meant to link something? :smile:

Hello guys :)

I have a question, I was reading next can't get the tokens for auth with jwt from localstorage.

If I have a server for render just the first charge of my site with next. And I have another server for my api. This receive the user/pass from the client and give a jwt. In which cases I need to get the token in the server??

Why would I need token in the render server (next)?

If the client dont send the token to the api server, the api dont give the data, and the user can't get private info. I'm dont understanding why I need to send the token to the render server.

@kamilml

This might help. In general, you have two options:

  1. Give Next server JWT (possibly via cookie). This will allow Next server to make api calls on the client's behalf. You would want this if full server side rendering is important to you.

  2. Store JWT in client local storage and don't give Next server access to it. In this case, you can simply bypass the api calls server side and postpone full render until client side loading is complete.

Apologies for re-opening this, but I thought I would add my 2 cents to this thread, and how my initial R&D is panning out in this area. Less code examples, more high-level flow.

First, for context, most of our app is already built in Symfony 3 (PHP), and uses Vue for a hybrid experience. The server renders a wrapper page, and assigns app data to __INITIAL_STATE__ for the app to pick up. This has resulted in making a decision between rendering marketing pages in Symfony (for SEO), and choosing UX/UI over SEO in other areas by fetching data via JS and providing a more SPA-ish feel. Also worth noting, not all pages are a binary public/private (as I've seen in some examples). Some pages are public by default, and then render differently if authenticated. We were considering using an SPA for parts of the site, but in many practical ways, it was a worse UX/UI (slower TTI, progress bars, etc.). Plus, that doesn't solve the SEO problem, unless we introduce FOUC and render text twice (once through Symfony, once again as JS components), etc..

Enter SSR / Universal JS...

In my case, what I did, was to mimic the PHPSESSID logic, by creating an HttpOnly UJSSESSID cookie (made the name up), and set the value to the user's JWT. The Symfony app passes this along in each page request as the user navigates around the site. When the user hits the UJS pages, the server-side of those apps will receive the cookies in the request (per the browser's built-in behavior). If the UJSSESSID cookie is set, the app calls the API to get the user information (eg. /api/v1/users/me with passing the token via Authentication header). The rest of the calls are done through the API, using the same token. The Symfony logout mechanism clears the UJSSESSID cookie. The next time the UJS app loads, it will render the pages in anonymous user mode. FYI, the Symfony vs. UJS page routing is done through Apache's ProxyPass. LocalStorage isn't used.

The end result is a seamless UX where the user is on some pages that are PHP with client-side JS, and some pages that are UJS. This enables us to do A/B tests, and iteratively update the site – not everyone gets to start from scratch :)

While this is a bit more complicated due to the symbiotic PHP/UJS, the same principle can be used in a full UJS solution with an API or Node.js server middleware (eg. Express, Adonis, etc.). Instead of setting the UJSSESSID cookie via a PHP page request (HttpOnly flag), have the user login through your SPA/UJS, and set the cookie there. What you SHOULD NOT do, is use your app to decode the JWT, or make 3rd party calls that require a client_secret. Use a middleware that stays on the server for that.

Hope that helps someone. Other examples I've seen were a little too petri dish for me.

@jaredpalmer Hey thanks for that implementation , I tried it , just copy-pasted all your code replaced your dashboard.js with my index.js which looks like this:

const index = () =>
  <div>
    <span>WoooHoooo</span>
  </div>

export default withAuth(index)

and in withAuth hoc i changed it to redirect to the signin page.
But before it redirects to the sign in page the contents of the index page still flash for a bit. :S

What's the status of this issue? 😇

This is bit overwhelm for new people reading through the whole discussion. I decided to implement the very basic authentication at here. Only 2 pages (index, login), and a custom server
https://github.com/trandainhan/next.js-example-authentication-with-jwt

Basically, we have a authentication middleware in server to check token in header of every request. The jwt-token will be store in cookies. I find this is very simple, straight forward and work very well.

@trandainhan Could you add a POST endpoint that uses a secret token to prevent CSRF attacks?

@sbking Updated source code with an example endpoint protected by CSRF attacks

Is this ready to use 😬?

Anyone tried auth with redux-auth-wrapper?

Hello everybody! Over the last months I created, and now refined

It features:

  • Registration with email and password
  • Login with email or username and password
  • Account page where you can set your username, change your password, and resend a verification email
  • Forgot password/reset password pages
  • Verify email page
  • A basic GraphQL API linked to a MongoDB (1 file, can be easily removed)
  • Minimal boilerplate (as much logic as possible is encapsulated in libraries)

Check out a live demo here: http://staart.nmr.io/

I did it mainly for myself for rapid prototyping, to get started quickly with an application with a simple, working accounts system that doesn't rely on external services. I'm quite happy with the results. I intend to keep using and maintaining these libraries, so give it a go if you feel that an established accounts system + UI for node is still missing.

@trandainhan Thanks, that's a really great example and much simpler for a lot of folks and would work in a lot of scenarios.

I'm going to have a think about if/how I can adapt the current logic in nextjs-starter to use something like this instead but securely, while still being compatible with express-session logic for real world use cases I have (like using things like Google oAuth APIs where I need the server to keep and track the tokens granted on first login).

I haven't figured out if that's possible yet, but it would be a lot easier for folks if it is.

If not, it's at least worth a nice write up somewhere to explain to folks the different options.

@trandainhan: If I add <Link href="/">Home</Link> into login.js, and then click the Link that is generated, I can then access the index.js without being logged in. How would you suggest fixing this in your example?

@iaincollins I see most solutions here authenticate against an oauth service. Is there any good solution for authenticating against an API that relies on JWT?

@paulwehner I think that happens because @trandainhan has only handled server side auth routing. I'm still not clear on how client side routing works in next.js because everything is handled under the hood by the next/Link component.

@carlos-peru Under the hood, Link actually use next/router to push new path into browser history. Seem like most of things is handled by browser, there are nothing to do here for a middleware on server side. So far, i can only think of creating a our own Link component and do other stuffs whenever we change the url.

@carlos-peru You can always use custom server to have full control over routing.

Thanks @trandainhan and @kolpav! I got to have a better understanding after spending some time over the weekend. I also managed to implement a solution that relies on HOC that keeps the jwt token on a cookie, so both the server and the client can consume the api.

@nmaro I have a Backend with JWT authentication for a website in next.js and an app (react-native).

So. I think in the next model: The client obtains a Facebook token (The user accepts the facebook login) and then the client sends to the backend the token to check and validate the user token (passport-facebook-token in node-js backend). Then, if the token is good, the backend sends to the client the JWT generated in the backend.

The problem? Your tool can obtain the key from facebook and google? I'm using hello.js but is not compatible with next.js and all the world use passport-facebook. I think is a big mistake because the api server needs to be compatible with web client and mobile app.

Thank you

P.S: I can't obtain a invitation in your slack channel.

@hmontes here - an invitation to the slack channel for the ooth project (user accounts for node, in particular next.js) feel free to join. Ooth uses passport-facebook-token (not passport-facebook) and passport-google-id-token which I believe solves your problem.

@nmaro I want to help you with your project. I have a backend with Graphql with passport-facebok and passport-google-id-token too !!!

But. Do you know a compatible package for connect facebook/google client in next.js?

On the client side you could use a similar pattern than I used here: https://github.com/nmaro/staart/blob/master/packages/staart/src/components/login-facebook.js https://github.com/nmaro/staart/blob/master/packages/staart/src/components/login-google.js
(instead of oothClient.authenticate just send a post request to your authentication route).

@timneutkens would a PR with a minimal with-ooth example be welcome?

@nmaro in your example. Why you use componentDidMount instead of componentWillMount?

I'm making an HoC (provider) component for Google login.

Is componentWillMount only called on the client? Then it should be fine.

Okey. Thank you so much for your examples. Finally i can implement auth with social media.

Last question. I'm created an access_token and a refresh_token and i want to put in localStorage to save the data.

Where can i put this in next.js to check the login when the page refresh? In create-react-app i put that in index.js

...
import { loginUser } from './actions'
...
let accessToken = localStorage.getItem('access_token')
let refreshToken = localStorage.getItem('refresh_token')

if (accessToken && refreshToken) store.dispatch(loginUser({accessToken, refreshToken}))

ReactDOM.render(
  <ApolloProvider store={store} client={client}>
....

Thank you

With ooth, I don't store the facebook tokens, I only use them once with passport and then create a normal cookie-based user session.

Ah. Okey. I'm using JWT instead of sessions. So, I send access_token to the backend, it check if the token is valid in the service (google, facebook), then if the user exists in my app and send me back a JWT.

Thank you :)

I should add that it's dangerous to store JWTs in localstorage (because of XSS), better to send them as cookies if it's all in one server/domain, so the client javascript code can't get the JWT, but the browser will send the JWT automatically as cookies. JWTs are tricky (see long discussions above), that's why in ooth I use sessions.

I use a JWT only if ooth is used as an external authentication microservice (because cookies only work on the same domain), and even then I use it exactly once to then create a session with the server, so it's not stored anywhere on the client.

For increment JWT security you can use a refresh token (Taken from oauth2).

An Mobile app can handle Cookies?

@nmaro Okey. I understand you. Sorry for my error.

Do you have a tutorial or code to save the JWT Token into a Cookie ? I'm looking https://github.com/zeit/next.js/blob/master/examples/with-firebase-authentication (My auth system is with graphql)

Nope, sorry, I never did that.

@hmontes @nmaro
I wrote this for myself, but may help:
https://github.com/malixsys/mobazoo

Hey @malixsys thanks for sharing. Something I don't understand from your code: it seems like you would configure API_BASE_URL to something external right? If you authenticate towards that API I guess it would start a cookie-based session, right? But then, how do you transfer that cookie to the next.js server, assuming the next.js server is on an other domain?

Ah no, I see, you set /auth/signin in the same process. Ok, then it's basically the same approach I used for ooth with next.js / staart.

Actually no, I'm still confused: in /auth/signin you give back a JWT, but you never do anything with that on the client side. Later you "getUserFromCookie" but I don't see where you set the cookie.

@nmaro I just wanted a basic login to work with universal initially. I both set a jwt and a cookie for now. The cookie is used to do universal. I have yet to use the jwt in another axios call in this repo. I do in a private fork where API_BASE_URL points to another domain...
It’s all on my TODO list, with PWA.
Please feel free to open an issue or ping me here...

@nmaro cookie is set in saveUser() here: https://github.com/malixsys/mobazoo/blob/master/utils/auth.js

I know that this issue is closed, but i would like show my solution.
https://next-auth.now.sh/
I think it's a bit similar to the website of zeit.co

Apr 9

I have done some work on it, currently swamped with other stuff 😥 By all means this is pretty high on my priorities list for Next

Sep 22 #2974

We're planning to release an official auth example soon, could you release this as separate repository? Thaaaanks!

@timneutkens

Hi, sorry to bug you about this again but could you please share how is the status of official auth example? It is definitely something I would love to see and judging by the count of comments in this issue others as well. So how high is it on the priorities list and should we get our hopes up? 😄

Hi @kolpav, they’ve officially announced this as on the roadmap for Next.js 5.

Finally, we're adding some highly requested examples (like user authentication), improved documentation for Next.js internals and smaller features and bugfixes.

https://zeit.co/blog/next-canary#the-roadmap

@babenzele That's great news. I must have missed that 😅

Where can we stay up to date with the development of Next.js 5? I'm integrating auth0 at the moment, but would be great to have an official nextjs auth path / example to follow

It seems Next.js is in desperate need of an official local authentication example using JWT/cookies/localStorage (or a combination of them, as long as it is secure and protected from XSS/CSRF)... I spent several weeks trying to come up with this using a separate Express API server with a Passport.js Local Strategy. I tried JWT in a cookie/localStorage for stateless requests, and I also tried a regular cookie with the session ID from the express-session middleware in it once I finally gave up on JWT and statelessness. I wound up running into problems where extra sessions were being created on the Express API server due to the way express-session works (tried with saveUninitialized: false). I considered moving the Express code into server.js in my Next.js app, but I would really rather it be a separate server. I'm also pretty sure my implementation isn't safe from XSS/CSRF or cookie hijacking. We need an official example that covers best practices for local authentication/login, or perhaps an official module as part of Next.js that will handle the complexities for us!

While we are awaiting Next.js 5.x and further examples you might want to take a look at https://nextjs-starter.now.sh which is still actively maintained and uses Next.js with Express, Express Sessions, CSRF (CRSF Tokens), XSS (HTTP Only Cookies for session tokens) and uses Passport JS to support oAuth and Email.

I'm actually in the process of refactoring the auth code out into a module, making it really easy to add authentication to Next.js projects in an easy to use way. The module should allow you to use it easily with any database you like, without having to copy a bunch of code from the Starter Project example. I'm hoping to be done with it this week.

For reference, the "Double Submit Cookie" method provide an easy way to add CSRF protection if you are looking for a simple but secure approach.

The issue with JWT in localStorage is that it is always readable from client side JavaScript, so a vector for session hijacking via XSS, if you have untrusted content (e.g. user submitted content or adverts).

If you use HTTP Only cookies for session tokens - or something like an JWT with a token value in an HTTP Only (or encrypt the entire JWT and decrypt it using an HTTP Only token on the server) - then sessions are as protected as they can be. The idea is just that, ideally, session tokens specifically should not be readable via client side JavaScript.

Of course plenty of sites don't use HTTP Only cookies because it's a pain for Single Page Apps, and invariably involves having some auth logic in the front end, but it's still the ideal approach.

Another option is still https://github.com/nmaro/ooth which is actively maintained,already comes in packages and is being used in a couple of production apps.

@iaincollins I downloaded Next.js Starter Project and started looking through it. I'll need to separate out the important security-related (XSS/CSRF) parts and try to integrate them into my application, or wait until you finish the separate module. Is there somewhere I can keep track of the development of that module?

Hi @kelleg1,

The separate module is now published as the next-auth module to make it easier to use in other projects. It it includes an example project that shows how to use it.

For further reference, the nextjs-starter.now.sh project now also uses the next-auth, which greatly simplifies the code in the starter project - and it's now much easier to add support for new oAuth providers or use it with different databases (although the example still uses Mongo DB).

It's still somewhat complicated though so if you have an existing app you might find it easier to use as as reference, but if so I hope it helps

NB: CSRF is currently still pretty tightly coupled in it. It uses lusca so assumes res.locals._csrf is set, but different CSRF libraries use different private vars.

I appreciate it's still more complicated to use than anyone would like as it is, but at least now the auth code is finally separated out into a module so I can start to refactor. I hope to make it easier to use (with default handlers for different databases, and easier configuration of oAuth) over time.

@iaincollins it looks like the only dependency on next.js is https://github.com/iaincollins/next-auth/blob/master/index.js#L342? If so, it would be great to make the lib next.js-agnostic.

@sedubois Totally agree!

Although that's probably a good discussion for it's GitHub repo issues rather than here. 🙂 If you'd like to suggest ways it could be improved, simplified and made more generic would love to collaborate.

(Having something that is as easy to use with next as possible is still a primary goal, but I don't see that it needs be exclusive, even if it ends up with next specific focused options too.)

@timneutkens Congrats on the release of nextjs 5. In the near future will we see the addition of an official local authentication example added to the examples folder? Or is this something still in the works for a later release?

Ooth now has a comprehensive documentation, including the specifics of next.js authentication.

@jaredpalmer I like your approach, I copied parts of your implementation. However, is getUser method safe? What if someone changes string within localstorage by hand, would it be smart for application to rely on this? Would it make more sense to change it into method that decodes JWT public part of token every time and reads user state from there? That way we can trust this state more due to JWT nature of things. What is your opinion?

You should store it in a cookie. I wrote that before Next had support for custom servers.

@jaredpalmer Can you tell me more about it? Should we store everything in cookies rather than local storage? Would your HOC be different too? Does it mean that we can now use getInitialProps method for server side rendering protected websites?

Just a heads up _if what you are talking about storing is sensitive data in local storage / Web Storage_:

"Never store sensitive data using Web Storage: Web Storage is not secure storage. It is not “more secure” than cookies because it isn’t transmitted over the wire. It is not encrypted. There is no Secure or HTTP only flag so this is not a place to keep session or other security tokens."

Set the token in a cookie after login. use cookie-js. Then use express cookie parser on the server so that you can check for get req.headers.cookies.myToken or equivalent. In a hoc’s getInitialProps check if req exists, then grab the token from req.cookies, otherwise get it on the client from Cookies.get(‘mytoken’). At this point you’ll have access to your token on the client and server. Then you want to make a fetch/axios wrapper/instance and merge it with next’s ctx in getInitialProps so all your pages have a way to make authenticated isomorphic requests. You may also want to just get your user in the hoc too. So you don’t need to repeat that everywhere. You can then make more hocs if you need to for common entities like withUser(withTeam(Page))

getUser is a bad idea, you should only store the token.

@jaredpalmer I built an approach almost exactly like that and everything is working fine. The problem I’m trying to solve now is how to refresh the tokens. The API I’m working with has relatively short-lived tokens (2 hours) and I’m trying to get my head around some system to keep the user logged in while using the app.
Do you have any input on that?

You can also save a request on every page transition by not passing data through next.js for your user. To do this you would read the token from the cookie in server.js and attempt to fetch the user and passt that to next’s request handler. In document, get it from props and store it in JSON on like window.USER. Then in your hoc, you just read it from window when in client land. Lastly, you should use axios with a response interceptor that immediately wipes out your cookie upon a receipt of 403 code and reloads the page

@pbrandone Albeit repetitive, the simplest & most predictable solution is to send your token with every request, receiving a newly-refreshed token in the header/body of the response. Your client updates its known value on every request/response cycle, effectively granting single-use tokens which are never stale & can't be misused.

Typically, with this approach, the /token/refresh is only used for the Application's initial "wake up" (think componentWillMount hook for <App/>), checking to see if the last-saved token is still valid & usable.

Never had to deal with refresh tokens, but I would probs try to do it in an axios interceptor. Would have to think about it more, but in theory you would intercept a bad req, then use refresh token to get a new token, set the cookie with the new token, and the replay the first request with the new token again. Axios interceptors are really powerful because they allow you to keep stuff like this abstracted and out of sight from your product code. You might need to write a request and response interceptor and/or hold some sort of stateful in progress object/map which could get hairy. Hopefully that helps

Or do what Luke said. Much easier.

If you want to do refresh tokens, you'll need to intercept the cookie (if you are using cookies) through a custom server setup. I did this using express. Otherwise you can do it all client side using JWT tokens. Since I was doing mine through cookies I had 2 use cases - handling navigation requests on client side, and page requests on the server-side(anytime someone manually types in a url or hits the page refresh.) But basically they are the same flow just executed differently since one is client-side and one is server-side. On the server-side since I used express, I just created a middleware to process the cookie, decode the JWT inside it and check the expiration. If it was expired, make request to get new token and continue to pass the decoded user to redux on the client. On the client I have an HOC wrapper for secure links that is constantly checking for the user, each time you navigate some where on the client. The HOC will get your cookie or JWT, (in my case my JWT is in a cookie) and check if its still valid quickly. If its not valid, it will try to get a refreshed JWT/Cookie, and continue, otherwise it will log you out. Yes more complicated, but also more secure. Hope that helps! Feel free to ask questions if you're interested - it took me awhile to figure it all out. I personally wonder about loggin people in with Github or FB etc - probly ok for a lot of cases but in some instances its just not professional. I have yet to see a bank, healthcare, any of my bills I pay etc - log me in with my FB account. Could just be a matter of time though ;)

As for refreshing access tokens;
I am not sure if this is a proper way to do it, but my access tokens are very short-lived (a minute or so). To deal with client-side token expiration issues I've taken this approach;
Each time a page is loaded it checks if token is expired, if so, it refreshes it. However, if it's still valid it calls method that checks how much time until expiration, and sets timeout that will refresh access token upon expiration. And then repeats this process as long as visitor is on the site.

callRefreshAuthenticationLater(){
    clearTimeout(this.accessTokenRefreshmentTimeout)
    this.accessTokenRefreshmentTimeout = setTimeout(() => {
      this.refreshAuthentication()
    }, this.authService.howMuchUntilExpiration(this.authService.getToken('access_token')))
  }

This returns miliseconds until expiration:

howMuchUntilExpiration(token){
    return normalizeTimestamp(jwtDecode(token).exp) - Date.now()
  }

Any feedback to this approach would be very welcome

@kunokdev - I think thats a good start. I moved a similar func I had to calculate remaining time to a func that just returned a boolean depending on if it was expired or not and that made my code a little more readable and simple plus I did not need to worry about resetting timers. Then I just check if the user is expired or not on any sort of request that requires auth. If I did have a countdown timer or something like that which was visible for the client or debugging then your function would be ideal. Thats my 2 cents 👍

Shouldn't the auth/login example be the most basic one possible? i.e. memory store and no JWT or weird token things. Usually when you think about someone "logged in" it means they have a cookie/active session.

I have a login HOC working client & server side. It uses a JWT and a backend (can be node, django, whatever).

Check it out and any feedback is highly appreciated

https://github.com/hugotox/AppStarter

Relevant code here https://github.com/hugotox/AppStarter/blob/master/src/components/auth/login-required.js

@hugotox I like the idea of using Redux store for storing auth data but when you call store.dispatch from getInitialProps several times be ready for undesired side effects, when decorated component will be rendered even if auth process isn't finished yet. Let's take usage example from your code:

export default withRedux(initStore, mapStateToProps)(
   loginRequired([PUBLIC])(MyPage)
)

In getInitialProps you call verifyToken before verificationOk, so MyPage.mapStateToProps will be called 2 times (if it listens for store.auth.user) and first time store.auth.user will be null even for a page that require logged in user.

Yesterday I also released first WIP version of own Next.js powered starter kit, but I started with Docker, Flow and fast-redux: https://github.com/dogada/microchain

I use there dedicated Docker images for API server and webapp that can run on different domains, so I still looking for a working solution that will allow to login real users but also issue OAuth bearer tokens for mobile clients for example.

@spencersmb excellent cheers! This is pretty much exactly the way I was going about it. In terms of the server side rendering for a URL on first time of visiting (never logged in), are you just intercepting it, seeing that there's no cookie and redirecting to a pages/login.js type page for instance?

@james-ff Thanks for posting that, even if it feels like yelling into the void.

It's been said in this thread a few times but everyone is apparently still ignoring that they shouldn't be storing session tokens in JWT with localStorage or in JavaScript accessible cookies and seems intent on trying to re-invent sessions and authentication in a less secure way (and that requires client side JS). 🙃🤦🏻‍♂️

The author of this post did a great write up with what's wrong with that in 2016 but sadly things don't seem to have improved:

Unfortunately, it seems I've found the upper limit on article length before people stop reading - many of the commenters on Reddit and Hacker News kept suggesting the same "solutions" over and over again, completely ignoring that they were already addressed and found impractical in the article itself.

Both the original article and the followup diagram are pretty great though.

@iaincollins JWTs and session-less apps/authentication have their advantages and plenty of people/companies (including Google) still want to use them. Having read the case against JWTs, I still think there should be two official next-auth examples/libraries, one using sessions (like next-auth) and one using JWTs, or perhaps just one that allows using either. The advantages, disadvantages, and caveats should be clearly explained for each on official Next.js tutorial pages and/or module documentation somewhere. Until someone invents a better boilerplate/library, I will probably continue to use nextjs-starter and next-auth, since they are the best I have found.

I've only been casually keeping tabs on this, but my experience with universal JS auth is to store a JWT in an HttpOnly cookie. There's really no reason to use localStorage when you have the option of using the server-side of the app to store the cookie. If you need access to the JWT for cross-origin API calls in the browser, then you can pass the JWT in an __INITIAL_STATE__ type scenario (be aware of XSS vulnerabilities, but at least you're not "storing" it client-side). For same-origin access, the cookie will be passed back and forth (assuming you are using withCredentials for axios, or credentials: 'include' for fetch), negating the need to put the JWT in JS at all. You could use a proxy on the server-side of the app to grab the JWT out of the HttpOnly cookie, _then_ make your cross-origin API calls as well. You negate the preflight call in that scenario as well. To each their own, but I personally don't think localStorage is needed for universal apps.

@bjunc Yes, out of all the options as to where a JWT should be stored (localStorage vs. HttpOnly cookie vs. Redux, or what have you), I might be wrong but I think the answer should basically always be an HttpOnly cookie. This seems to have been stated countless times on numerous blogs and forums. (I'm not sure about refresh tokens--perhaps they should be stored in an HttpOnly cookie as well?) I would think where JWTs are stored should be a resolved matter that is apart from the rest of the advantages/disadvantages of JWTs.

I tried to do more or less exactly what you said in a project of mine before I started to adopt nextjs-starter and next-auth. It's been a while but if I remember correctly I think the problem I encountered was that my Express API server that I was authenticating against (which was using express-session) was not initializing/fetching the session correctly. I would get weird behavior where sessions would be initialized multiple times. I intend to resume doing more or less what you described if I can fix that. I will also continue to work with nextjs-starter and next-auth, since sessions do eliminate many of the other concerns JWTs pose, such as how to invalidate tokens.

Again, an official example (or examples) would be helpful. Key word: official, meaning the authors of Next.js have considered all possibilities and incorporated the best ideas and practices into it. Ideally using modern ES6/ES7/ES8 features such as promises and async/await.

@kelleg1 it sounds like your issues are related to the specifics of cookie creation. For instance, you can accidentally create multiple cookies with the same name by using different settings (HttpOnly, domain, path, expiration, etc.); which could create odd side-effects like you are describing. One way to debug that is to use the dev tools Application->Cookies. If you see a bunch of cookies with the same name, then that might point you in the right direction.

Anyway, I'm not on the core team, so I can't help with an "official" contribution (and in-fact, I'm using Nuxt.js, not Next.js), but the principles are the same. I experimented with a few different ways of doing this (weighing the pros/cons of JWT, cookies, CSFR, websocket CSWSH, localStorage, etc.). I ultimately came to the conclusion that the universal nature of Next/Nuxt lended itself well to the use of HttpOnly JWT cookies. Maybe others would come to a different conclusion, but I'm personally not in the camp of "oh god, don't use JWT, didn't you read that article that says that JWT gives you cancer!?".

@iaincollins sorry to bring this back again but every single tutorial on the web uses localStorage to save the token and you're saying is insecure. If so, where are we supposed to store the token?

COOKIES! :D
we use something like this:

// auth.js
async signIn(res, info) {
    const { email, password } = info;
    const result = await this.service.signIn(email, password);
    if (!result) {
      res.status(HttpStatus.BAD_REQUEST).json({ success: false, message: 'Wrong email and/or password' });
      return;
    }
  const payload = {
    scope: 'appUser',
    userId: user.email,
    language: user.language,
    iat: moment().unix(),
    exp: moment().add(7, 'days').unix(),
    type: user.type,
    status: user.status
  };
    const token = jwt.sign(payload, ...);

    res.cookie('token', token, { domain: 'yourdomain.com', path: '/', secure: true, httpOnly: true, maxAge: 24 * 60 * 60 * 1000 * 7, signed: true });

    res.status(HttpStatus.OK).json({});
  }

// pages/index.js

class Index extends PureComponent {
  render() {
    return (
      <Layout title="home">
        <Home />
      </Layout>
    );
  }
}

Index.getInitialProps = async ({ req, res }) => {
  const auth = req ? getServerSideToken(req) : getClientSideToken();
  return { auth };
}
export default Index;

// utils/auth.js
const decode = ({ token }) => {
  const decoded = jwt.decode(token);
  const { userId, type = 'anonymous', status, language = 'en' } = decoded || {};
  return { user: { email: userId, type, status, language } };
};

export const getServerSideToken = (req) => {
  const { signedCookies } = req;

  if (!signedCookies) {
    return {};
  }
  try {
    return decode(signedCookies);
  } catch (parseError) {
    console.error(parseError, signedCookies);
    return {};
  }
};

export const getClientSideToken = () => {
  if (typeof window !== 'undefined') {
    const user = window.__USER__ || {};
    return { user };
  }
  return { user: {} };
};

// pages/_document.js
export default class extends Document {
  static async getInitialProps(ctx) {
    const props = await Document.getInitialProps(ctx);
    const info = getServerSideToken(ctx.req);
    return { ...props, ...info };
  }

  render() {
    const { user = {} } = this.props;
    const json = JSON.stringify(user);
    return (
      <html lang="en">
        <Head>
          <meta name="viewport" content="width=device-width, initial-scale=1" />
          <meta charSet="utf-8" />
          <link href="//fonts.googleapis.com/css?family=Kadwa:400,700|Work+Sans:100,300,600" rel="stylesheet" />
          <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.14/semantic.min.css" />
          <link rel="stylesheet" href="/static/site.css" />
        </Head>
        <body>
          <Main />
          <script
            dangerouslySetInnerHTML={{ __html: `__USER__ = ${json};` }}
          />
          <NextScript />
        </body>
      </html>
    );
  }
}

// somewhere.js
import axios from 'axios';
axios.defaults.withCredentials = true;
axios.post(`${apiBaseUrl}/some/path`, data)...

// somemiddleware.js
      const { signedCookies = {} } = req;
      const { token = '' } = signedCookies;
      if (token) {
        try {
          const info = jwt.verify(token, ...);
          req.user = await this.getUser(info.userId);
          if (req.user) {
            next();
            return;
          }
        } catch (err) {
          console.error({ permissionError: err });
          return res.status(500).json({...});
        }
      }
      return res.status(403).json({...});

A little kludgy but pretty secure since client side javascript never sees a token and cookie is httpOnly, secure, signed and sent automagically by axios or other...

Hi all, just created an auth example with cookies and redux. See here https://github.com/zeit/next.js/pull/4011

Wait so where did this issue end up? I never could find an Express auth example in the official repo.

I see lots of talk of cookies and sessions, but how does that work for my native mobile app when it needs to hit the API?

It can be done, but that's what JWT is for :p

I've just done a detailed read of this entire thread and feel compelled to summarize my findings.

It appears there are two viable starter projects with solid auth modules factored out:

Excellent work by @iaincollins and @nmaro !

Thanks @curran :)

I documented all the code changes I made for gutting the extraneous aspects of nextjs-starter in this Pull Request https://github.com/iaincollins/nextjs-starter/pull/86

This may be getting closer to a solid bare-bones authentication example for the Next.js repository.

I had a requirement recently to implement OAuth with Office 365, so I thought I'd share a very simple example I threw together here. It needs work (and is nowhere near as developed as some of the above examples) but I think it could be generalized eventually to use various OAuth clients as well. It utilizes several of the examples already shown in the Next.js examples repo as well as the protected routes thread here. In any case, just figured I'd share in case someone wanted a quick example of how to (maybe) do this with Microsoft.

For anyone interested in a simple JWT tokenized authentication that works client and server side, I got some help over in the Spectrum chat and figured I'd share it with you all. Any feedback is always appreciated.

https://github.com/bgold0/nextjs-auth-skeleton

Hi guys!
Here is another example of authentication with next.js I've built a couple of months ago, maybe someone will find it useful:

https://github.com/alan2207/nextjs-jwt-authentication

Ooth 2.0 is out with new and better docs

Ooth is a user identity management system built for node.js (with next.js in mind).

Currently supported strategies:

  • Primary: username/password (incl. verify email, forgot pw etc.), guest, facebook, google
  • Secondary: cookie-based sessions, JWTs

Check out this live example that puts it all together with next.js (source code).

Many of the auth samples here (and in the examples folder) return the user session/token/etc from getInitialProps. As far as I understand, when the page is rendered server-side, this means that the user session info will get sent as part of the HTML page response (as part of NEXT_DATA) to the browser.

I see two security issues with this pattern when getInitialProps is run server-side:
1) The user session gets transmitted over the network from server to browser. If any such requests are using http:// and not https://, the user's token, etc would be exposed over the network in plain-text.

2) The user session is returned from server to browser as part of the HTML page (in a NEXT_DATA script tag). Having the user's token/etc sitting directly on the HTML page seems risky, especially once the page is parsed, rendered by the browser, and other 3rd-party scripts may now be running.

Are these issues that have already been addressed? Are there any mitigations for these threats?

That's why I use cookies. See my example here https://github.com/hugotox/nextjs-starter/blob/master/pages/_app.js

Finally! Here you can find a fully dockerized next.js auth example with the following containers:

  • next.js
  • auth microservice (ooth)
  • api (graphql)
  • session storage (redis)
  • small reverse proxy

Everything is pulled together with docker-compose.

Quick note on JWTs. JWTs have the advantages of being stateless, good for mobile and good if you need to pass credentials from one domain to the other. The main disadvantage is that they expose the browser to XSS. For this example I opted for a pure cookie-sessions based solution. I still managed to split things up in microservices thanks to the reverse proxy (all microservices run under the same domain) and to the shared session storage.

https://github.com/nmaro/staart/tree/master/examples/staart
The live example is, as usual: https://staart.nmr.io

I think it is prudent to clarify, using JWT does not intrinsically "expose" you to XSS. Rather, if your site has an XSS vulnerability (not due to JWT itself), a JWT could be compromised (along with any other script accessible information); whereas httpOnly cookies will not be accessible. Nevermind that you can use a JWT as the value of an httpOnly cookie!

Cookie-only solutions can work well for same-domain communication, but if you have a headless API (eg. example.com calling api.example.com), then that's not really a solution unless you want to proxy browser requests from example.com to api.example.com by making your API calls to example.com and forward them on with the cookie appended to the request (which comes with its own set of pros/cons).

I personally feel the cons of JWT are vastly overblown, and are pretty easily mitigated via a slew of commonly implemented protections. Not least of which, a token blacklist referencing the jti claim (eg. Version4 UUID) in the event that the token has been compromised prior to expiration.

Hey @bjunc yes thanks for clarifying. Correct, JWT doesn't itself expose you to XSS. Regarding your other observations, I'd like to add that they each have their pitfalls.

Nevermind that you can use a JWT as the value of an httpOnly cookie!

Yes, would just like to add that that removes the one advantage of JWT of transferring credentials between domains.

a token blacklist

That, and the other common practice of a refresh token remove the other advantage of JWT of being really stateless.

This is my understanding of the situation:

  • If you need cross location auth use JWT <- but if possible avoid because of security
  • If you need to auth from any client that is not a browser and doesn't have the XSS issue (e.g. a desktop or mobile app) use JWT
  • Otherwise use cookie-based sessions, because a JWT based solution will either be less secure (XSS) or not be really stateless (blacklists), or require you to use a proxy to keep everything under the same domain anyway.

I wrote a Medium article on Next.js Authentication/User accounts.
It's an extensive tutorial and my braindump from almost two years of spare-time development and thinking (my first comment on this issue is from February 2017).

https://medium.com/the-ideal-system/user-accounts-with-next-js-an-extensive-tutorial-6831cdaed16b

You keep correlating JWT with being less secure. JWT does not make your site any less secure. It is not in and of itself a XSS vulnerability. If you have an XSS exploit, you are in equal trouble regardless. Even with an httpOnly cookie, the attacker may not be able to read the cookie value, but it doesn't matter because they can run arbitrary code – like XHR requests that would automatically pass the session cookies (essentially acting as a CSRF attack). If your API is cross-domain, and you are making browser-to-server requests, then the JWT is somewhere in the code anyway (be it initial state, or localStorage). If you use Axios, you might have even set global defaults, where the attacker need only make an Axios request and not even worry about auth.

Also, you can't really talk about XSS without also talking about CSRF; where auth cookies are specifically targeted. Even with a reverse proxy setup, a CSRF attack would allow the attacker to execute authenticated requests against your API.

Btw, just because you put the JWT in a cookie (for say, knowing if the user is logged in), doesn't mean you are precluded from using the cookie value (eg. a JWT) for cross-domain server-to-API-server access on page load (which also works for the reverse proxy scenario). Nor are you or precluded from passing the JWT in initial state as well for browser-to-API-server requests. They're not mutually exclusive.

As an aside, I find the idea of "stateless" JWT both overrated and limited in application for the majority of use-cases. For instance:

  • Resource based / dynamic permissions (eg. not just "can edit Post", but rather "can edit Post:1634").
  • What if the user's account has been blocked / deleted?
  • Haven't paid their monthly subscription; which throttles functionality?
  • Blacklisted the token (per above)?

You're not baking all that into the JWT, which means you have to dip into the domain layer (ie. database) to find out. You just loaded the resource, so might as well put it where the rest of the app can get to it, and now you have state. I find it really silly to think everything you need to know about the subject would be baked into the token; while simultaneously keeping it mostly anonymous and lightweight. Without digressing too much, there's an argument for "stateless" requests in inter-service communication, but even that I find impractical (at least as it pertains to the concept of baking what you need to know about the subject into the JWT)...

Ooth authentication strategies available meanwhile (new in bold):

  • Local (Username/email/password)
  • Facebook
  • Google
  • Guest
  • Patreon
  • Twitter
  • Authy (Twilio) - passwordless via SMS

@jaredpalmer you wrote
Like php, the atomic unit of Next is the page. One of the coolest features is that it lazy loads each page only when it's requested. With client-side only auth but with server-rendering, the js for that protected page is in fact downloaded by the browser. In the future when Next adds server workflows, you'll hopefully be able to block render and redirect on the server to prevent this entirely. This will require cookies, sessions, and AFAIK session stores, but that's just the cost of doing hybrid apps like these.

We are 2 years later. Is there a server workflow to prevent loading js for protected pages?
@timneutkens maybe puting protected content in other zone?
How would I entirely prevent access to protected content ?

@lishine You have a ServerResponse in the getInitialProps of your page - you can easily redirect someone unprivileged.

Is there an example of auth with redux?

Is there an example of auth with redux?

You can try this example which is using redux, and check out if it works for you...
You can find it somewhere in this topic, but in case you can't find it, here it is:
https://github.com/alan2207/nextjs-jwt-authentication

I think this is more complicated problem when using Server Side API call results getInitialProps, because Virtual DOM uses old results after LOGOUT-LOGIN action. I'm thinking about solution

_EDITED_
and here's my answer with redux-observable

|Side|Auth|TODO|
|---|---|---|
|Server|true|Fetch initial data (with request cookie proxy).|
|Server|false|Show login page, and fetch data after logined.|
|Client|true|Fetch initial data.|
|Client|false|Show login page, and fetch data after logined. (This happens only when session expired in page to page moving)|

  static async getInitialProps({ store, query: { rowsPerPage, pageIndex }, req, auth }) {
    store.dispatch(TemporaryStoryActions.initPageState());

    const isAuthenticated = () => req ? req.isAuthenticated()
      : store.getState().auth.isAuthenticated;

    if (isAuthenticated()) {
      // fetch initial data
      const TemporaryStoryApiProxy = withCookieProxy(req, TemporaryStoryApi);
      await TemporaryStoryApiProxy.fetchTemporaryStories({
        rowsPerPage: rowsPerPage || 15,
        pageIndex: pageIndex || 0,                                                                                                                                  }).then(json => {
        store.dispatch(TemporaryStoryActions.loadTemporaryStories(
          json.rowsPerPage, json.pageIndex, json.count, json.rows));
      }).catch(error => {
        if (error.response && error.response.status === 403) {
          store.dispatch(AuthActions.initState(false, null));
          return;
        }
        throw error;
      });
    }

    if (!isAuthenticated()) {
      // => if client side fetch failed with 403, isAuthenticated() turns off to false
      // register logined action for client side login succeeded
      const reloadAction = TemporaryStoryActions.fetchTemporaryStories({
        rowsPerPage: rowsPerPage || 15,
        pageIndex: pageIndex || 0,
      });
      store.dispatch(AuthActions.addLoginedAction(reloadAction));
    }

    return {
      ...store.getState(),
    }                                                                                                                                                           }                                                                                             }
export const withLogin = Page => class SecurePage extends React.Component {
  static async getInitialProps (ctx) {
    if (ctx.req && ctx.store) {
      // server side
      const isAuthenticated = ctx.req.isAuthenticated();
      const { user } = ctx.req;
      ctx.store.dispatch(AuthActions.initState(isAuthenticated, user));
    }
    return Page.getInitialProps && await Page.getInitialProps(ctx)
  }

  render () {
    const { auth } = this.props;
    return auth.isAuthenticated ? <Page {...this.props} /> : <LoginPage />
  }                                                                                                                                                           }
// when [front-end server] => [api server]
// on Server Side Rendering,
// needs to proxy Cookies which sent to Next.js page request
export const withCookieProxy = (req, targetApi) => {
  if (!req) {
    return targetApi;
  }
  targetApi.client.interceptors.request.use(config => {
    const cookieString = Object.keys(req.cookies).map(key => `${key}=${req.cookies[key]}`).join('; ');
    const headers = {
      ...(config.headers || {}),
      Cookie: cookieString,
    };
    return {
      ...config,
      headers: headers,
    };
  }, error => {
    return Promise.reject(error);
  });
  return targetApi;
};
const loginEpic = (action$, state$) => action$.pipe(
  ofType(AuthActionTypes.login),
  mergeMap(action => {
    const email = action.payload.email;
    const password = action.payload.password;
    return from(AuthApi.login(email, password))
      .mergeMap(json => {
        const user = json.user;
        const loginedActions = state$.value.auth.loginedActions;
        const successActions = [
          AuthActions.removeAllLoginedActions(),
          ...loginedActions,
          AuthActions.loginSuccess(user.id, user.name, user.last_login_date),
        ];
        return from(successActions);
      }).pipe(catchError(error => {
        return of$(AuthActions.loginFail(error));
      }));
  }));

Seems complicated when something simple would do, along the lines of :

export const withAuthSync = WrappedComponent => class extends Component {
  componentDidMount() {
    window.addEventListener('storage', this.syncLogout);
  }

  componentWillUnmount() {
    window.removeEventListener('storage', this.syncLogout);
    window.localStorage.removeItem('logout');
  }

    syncLogout = (event) => {
      if (event.key === 'logout') {
        console.log('logged out from storage!');
        window.location.reload();
      }
    };

    render() {
      return <WrappedComponent {...this.props} />;
    }
};

This example was merged as part of Next.js 8
https://github.com/zeit/next.js/tree/canary/examples/with-cookie-auth

@timneutkens thanks for the link.

looking at https://github.com/zeit/next.js/blob/canary/examples/with-cookie-auth/www/utils/auth.js#L26-L34 ... shouldn't there be some kind of check after auth() was called?

Testing the example without a cookie leads to Profile.getInitialProps() being called, whereas I thought the redirect would happen before even trying to get more "initial props" ...

I made an example here that has server-side pre-rendering + authentication w/ apollo

https://github.com/HorizonShadow/apollo-next-now-test

Please keep in mind that OWASP security guidelines recommends against storing JWT token in Local Storage, i.e. "A single Cross Site Scripting can be used to steal all the data in these objects, so again it's recommended not to store sensitive information in local storage."

Here's Auth0: Where to Store Tokens and Tom Abbott: Where to Store your JWTs – Cookies vs HTML5 Web Storage.

Here is an example with Nuxt.js + Express.js proxy server + Django backend. Where Express server is used for proxying auth request to the actual backend and is handling CSRF protection when using JWT token storing in a cookie (imposes some restriction on the token's length / how much info can be stored in the JWT token): https://github.com/danjac/nuxt-python-secure-example

@timneutkens I need some docs on how to send token from cookie 🍪 to SSR custom redux middleware. I am getting the cookies inside _app.js . But how should I pass it to customApimiddleware. Where I have written fetch requests. Thanks

I wrote a Medium article on Next.js Authentication/User accounts.
It's an extensive tutorial and my braindump from almost two years of spare-time development and thinking (my first comment on this issue is from February 2017).

https://medium.com/the-ideal-system/user-accounts-with-next-js-an-extensive-tutorial-6831cdaed16b

I think this is one of the best tutorials for handling authentication in a nextj.js app. I've seen things like storing tokens to localStorage (XSS), storing tokens in cookies (without handling CSRF), and even storing tokens in cookies from browser (both XSS and CSRF vulnerable).

I really like your solution with the reverse proxy and sharing the session info between different services. I really would like to not create a custom server for next.js app, but I think it is the most straightforward way to both handling sessions and prevent csrf (and maybe adding the reverse proxy). I may even end up creating a monolith project (both for rendering the app and handling db operations etc).

I've seen that some people (including ZEIT) keep the APIs stateless and let the next.js app to handle the session. It passes the tokens to APIs. But going with sessions only makes things a little bit more tight and less complicated.

It would be a really better thing to have a full authentication example for next.js. With things like authentication for external APIs, keeping the session in next.js app, sharing session between services or passing tokens to them, and maybe even refreshing the tokens if they are expired. (A lot of people write a lot about JWTs and just use them in their tutorials, but they mostly don't even expire them or not even refresh them.)

Anyway, you've written one of the most complete tutorials on this subject. So, thanks!

I really hope there will be much more complete and clear examples and documentation.

It would be a really better thing to have a full authentication example for next.js. With things like authentication for external APIs, keeping the session in next.js app, sharing session between services or passing tokens to them, and maybe even refreshing the tokens if they are expired. (A lot of people write a lot about JWTs and just use them in their tutorials, but they mostly don't even expire them or not even refresh them.)

I, too, am at a loss as to which approach to choose.
Thank you very much for the link to the article.
Currently, which of the implementations have you settled on?
Have you found a comprehensive authorization example for next v9.3+?

Worth checking out Auth0's new cookie based approach
(Of course this is for a particular identity provider, but approach could be useful to see)
https://github.com/auth0/nextjs-auth0

  • Really cool that you can "proxy" api requests via nextjs's api routes (even via one dynamic route)
  • Then you never have to expose access tokens etc to the client side (as nextjs api routes only execute server side), server side can obtain access tokens etc through auth0 library and cookie with a secret
  • Your client side code would call your nextjs api routes, and the api routes would then perform the real api request

Bear in mind they say this approach is "experimental" in the ReadMe

Worth checking out Auth0's new cookie based approach
(Of course this is for a particular identity provider, but approach could be useful to see)
https://github.com/auth0/nextjs-auth0

  • Really cool that you can "proxy" api requests via nextjs's api routes (even via one dynamic route)
  • Then you never have to expose access tokens etc to the client side (as nextjs api routes only execute server side), server side can obtain access tokens etc through auth0 library and cookie with a secret
  • Your client side code would call your nextjs api routes, and the api routes would then perform the real api request

Bear in mind they say this approach is "experimental" in the ReadMe

This article is very helpful and it covers a lot of different architecures.
https://auth0.com/blog/ultimate-guide-nextjs-authentication-auth0/

Using API routes as proxy, login/logout via API routes, getting the token from API, setting it as HttpOnly cookie is a solid approach I think.
One concern might be CSRF, but you can easily create some solution with csrf npm package (not csurf, but that might work too).

@onderonur , thanks for auth0 article.
that is, at present there is a reliable or at least minimal example of implementation on pure jwt with next.js?
I don’t want to make an advanced layer with cookies and setting them up. In the csr application, we simply stored the token in localstorage and sent it along with the request.

@onderonur , thanks for auth0 article.
that is, at present there is a reliable or at least minimal example of implementation on pure jwt with next.js?
I don’t want to make an advanced layer with cookies and setting them up. In the csr application, we simply stored the token in localstorage and sent it along with the request.

I've used this method for one of my repos but it's still in draft, so make sure to test them yourself :)
https://github.com/onderonur/post-gallery
Actually the "cookie layer" is not an advanced thing. Just call your API's login endpoint through /api/login API route and if the request is successful, set the token in a httpOnly cookie.
You can check my repo for the exact same implementation.

One other option is (if you want to have nearly the same flow as setting the token in local storage), you can use js-cookie npm package, call your login endpoint with a client-side request, end if it returns a token, set it in a cookie. And when you make a request (through an axios interceptor etc) read the cookie value and pass it to your API as a request header. I've seen a lot of (and even some popular) applications using this approach. But this is a little bit non-secure. Because you can't set httpOnly cookies in browser. So, JavaScript will be able to read your token cookie. Thus, there will be XSS vulnerability.

Appreciate this is an old thread (and a long running topic in general) but for those looking for additional references or examples, we've picked up work on NextAuth.js v2 recently. I mention it not so much as a plug – it's an open source project and a bunch of folks have helped out on it – but it's super simple to use and the code and approach might be useful as a reference to folks.

For some background, like NextAuth v1, it uses cookies that are signed, prefixed and HTTP only, avoiding common security pitfalls of using client side tokens.

NextAuth.js v2 supports signing in with Apple, Google, Facebook, Twitter, GitHub, Auth0, Okta, Slack, Discord and other OAuth providers (it supports both 1.x and 2.x). You can use it with MySQL, MariaDB, Postgres, MongoDB - or no database all (just OAuth and JSON Web Tokens for a 100% serverless solution).

Usage is very simple, there is a Universal static method called session() and a React Hook called useSession() you can use in components client side:

import { useSession } from 'next-auth/client'

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

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

It is built for Next.js 9.x and Serverless, and doesn't have dependancies like Express or PassportJS. It includes an auth provider you can use in _app.js to automatically add authentication state to all pages; it works for both client and server side rendering.

For more info, see the next-auth.js.org or check out the next-auth@beta on NPM

It's still a work in progress - we are still polishing up the documentation and event model - with a target release date of ~early~ mid June.

Appreciate this is an old thread (and a long running topic in general) but for those looking for additional references or examples, we've picked up work on NextAuth.js v2 recently. I mention it not so much as a plug – it's an open source project and a bunch of folks have helped out on it – but it's super simple to use and the code and approach might be useful as a reference to folks.

For some background, like NextAuth v1, it uses cookies that are signed, prefixed and HTTP only, avoiding common security pitfalls of using client side tokens.

NextAuth.js v2 supports signing in with Apple, Google, Facebook, Twitter, GitHub, Auth0, Okta, Slack, Discord and other OAuth providers (it supports both 1.x and 2.x). You can use it with MySQL, MariaDB, Postgres, MongoDB - or no database all (just OAuth and JSON Web Tokens for a 100% serverless solution).

Usage is very simple, there is a Universal static method called session() and a React Hook called useSession() you can use in components client side:

import { useSession } from 'next-auth/client'

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

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

It is built for Next.js 9.x and Serverless, and doesn't have dependancies like Express or PassportJS. It includes an auth provider you can use in _app.js to automatically add authentication state to all pages; it works for both client and server side rendering.

For more info, see the next-auth.js.org or check out the next-auth@beta on NPM

It's still a work in progress - we are still polishing up the documentation and event model - with a target release date of ~early~ mid June.

Great work this!
Can this be used on the client side alone? For example, I have a rails API app - and use next JS for the client side.

Was this page helpful?
0 / 5 - 0 ratings