Auth0.js: auth0-js does not support SSR

Created on 26 Nov 2018  Â·  21Comments  Â·  Source: auth0/auth0.js

Description

When we use auth0-js together with nextjs, it complains.

Reproduction

Download the repro code here: 'https://github.com/jim-king-2000/nextbugrepro'. Type "npm install&&npm run dev". Then access "localhost:3000".

Result

The browser throws an error:

Unhandled Rejection (ReferenceError): window is not defined

Comment

Although the issue can be circumvented by simply adding:

if (typeof window !== 'undefined') ...

I insist that WebAuth should support server-side rendering.

All 21 comments

you can only call authorize from the browser, so it makes sense to throw an exception in that case. If you're using nextjs or any other SSR app, make sure to call Auth0.js methods only in the client side phase (componentDidMount for react apps)

No plan for auth0-js to support SSR?

@jim-king-2000 authentication has nothing to do with how you render your application. Auth0.js is a browser library. Because of that, it can only run in the browser.

Got it. Thanks.

@luisrudge I want to add my vote here.
I'm dealing with auth0, and have followed the Vue JS auth0 guide, very helpful!

However, I'm using Vue within Gridsome (similar to Gatsby, just for Vue)
And while everything works fine on dev mode, doing a build fails because window object doesn't exist in SSR.
I don't intend to use Auth0 in server side context, but due to the fact that I'm adding it as a Vue Plugin, and that happens in SSR context, my whole build fails because auth0-js has references to window.

@altryne I don't know how Gridsome works, but you have to find a way to initialize Auth0.js only when your code is running in the client.

@luisrudge thanks for the reply.
I've followed the guide to implement Auth0 with Vue (https://auth0.com/docs/quickstart/spa/vuejs/01-login#create-a-vue-plugin)
And in that guide 2 things happen, 1 we create an auth.js and 2 we are creating a Vue plugin.

I've also taken a peek at the Gatsby (react static site generator) (https://auth0.com/blog/securing-gatsby-with-auth0/) and in there, the article outlines steps of making auth0-js work in server-side rendering.

I couldn't reconcile the differences between the two tutorials and make it so auth0-js will only load on the client side, mostly due to the need to protect routes with an isAuthenticated check, and routes being created on the app server side rendering.

Hey @altryne,

The difference between the two tutorials is very subtle, but basically the technique used in the Gatsby case is to alter the Webpack config so that the auth0-js library isn't loaded at all when Gatsby is building for the server.

It's not easily linkable, but search for the heading "How to Set up Gatsby with Auth0", and you'll see a code snippet which shows their technique for excluding auth0-js from the Webpack build using Webpack's null loader.

The section that follows that — "Create an authentication utility" — demonstrates how to use the Auth0 type in such a way that it doesn't break your app if you're not running in a browser context. The technique is to basically check if the window object exists; if it does, create the Auth0 instance in the usual way. If window does not exist, return an empty object.

As for configuring this for your needs, it comes down to configuring Webpack so that it doesn't load the auth0-js library when building for the server. Vue has its own Webpack configuration hook, but you probably want to implement this on the Gridsome side. I'm also no familiar with Gridsome at all, but it looks they also have a Webpack config hook that you can use in order to provide the config you need to exclude auth0-js from the build.

Let me know if that helps you!

I solve this by redirecting to a login page, that invokes authorize client side:

class Login extends React.Component {
  componentDidMount() {
    auth0Client.authorize();
  }
  render() {
    return <div>Logging in..</div>;
  }
}

export default Login;

@jim-king-2000 authentication has nothing to do with how you render your application. Auth0.js is a _browser_ library. Because of that, it can only run in the browser.

The comment that SSR has nothing to do with authentication is simply not true. This article gives a great example of best practices for JWT that supports SSR. It's very unfortunate Auth0 doesn't support this.

https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/#ssr

@luisrudge I want to add my vote here.
I'm dealing with auth0, and have followed the Vue JS auth0 guide, very helpful!

However, I'm using Vue within Gridsome (similar to Gatsby, just for Vue)
And while everything works fine on dev mode, doing a build fails because window object doesn't exist in SSR.
I don't intend to use Auth0 in server side context, but due to the fact that I'm adding it as a Vue Plugin, and that happens in SSR context, my whole build fails because auth0-js has references to window.

@luisrudge I'd like to support what @altryne has said here. I have exactly the same problem in my React app. I don't need auth on SSR and so I can easily not _call_ the .authorize method, the fact remains that because it is import ed, it's still in the bundle, and thus causes an error because Superagent isn't supported in a non-browser environment. So, even if we stop .authorize being called, the fact that's it's still present causes SSR to die.

Thoughts?

@shankie-san I would investigate if there's anyway to exclude auth0-spa-js from the build when building for SSR, which is a common pattern we're seeing.

Hey @stevehobbsdev yes that would be a good pattern. In fact, that's kinda of what I've had to do in my own app is finding a way of excluding all of auth0 when rendering on the server (when it's really only the auth0-spa-js that's causing the issue)

We are also using Gridsome with Auth0.

in our main.js where we loaded the Auth0 library (as a Vue plugin) we put this around it:

if (process.isClient) {
    ....
}

This ensures it does not get loaded during the SSR process and only in browser.

@tyroneerasmus that's interesting because in my case, simply importing the package at the top caused it to error. And because in ES6, imports must be at the top of files (i.e. non-connditional), then it became impossible to stop the error.

@shankie-san yeah that is strange - I have my import at the top and it is happy. I must mention for completeness sake that this worked for me using @auth0/auth0-spa-js version 1.6.5 and then SSR broke in the 1.7x releases. Now with 1.8.1 it seems to work again.

Ah – that's a different package, right @tyroneerasmus ?

Yes it is :) but I thought the same thing might work in this case because this is how I have solved similar issues with a few libraries that trip up SSR in Gridsome

@shankie-san to get around this issue, you can dynamically import the auth0-js library from a browser-safe method. In my case, I had import { WebAuth } from 'auth0-js' at the _top_ of my auth module file and I would see the following error log:

Using browser-only version of superagent in non-browser environment

To fix this, I made the following change

class AuthService {
   private auth0?: import('auth-js').WebAuth;

   public init = async () => {
      import('auth0-js').then(({ WebAuth }) => { ... });
   }
   ....
   public loginWithFacebook = () => {
      this.auth0?.authorize(...)
   }
}

You can have add a browser init file to your entrypoint so that it's executed in the correct context, which would target web and not node.

That's a lovely solution @ktranada . I didn't realise that import could be called like a function to return a promise.

@luisrudge would you consider putting an adaptation of @ktranada 's solution in the docs? I don't think that it's obvious for people who just want to plug Auth0 in to a Next/SSR app

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MaksSlesarenko picture MaksSlesarenko  Â·  3Comments

duongmanhhoang picture duongmanhhoang  Â·  3Comments

apamildner picture apamildner  Â·  3Comments

jorgeucano picture jorgeucano  Â·  6Comments

beno picture beno  Â·  3Comments