netlify/gotrue-js

Page refresh drops currentUser() using GoTrue with React (Gatsby)

tatwater opened this issue · 11 comments

Hi all, I'm trying to set up GoTrue with Gatsby instead of using the Netlify Identity Widget so that I can build my own custom components.

Everything seems to be working fine so long as I only click <Link />s, but if I refresh a page or type a new in-site URL then auth.currentUser() returns undefined, even though I've set setCookie to true.

My src/utils/auth.js file looks like this:

import GoTrue from 'gotrue-js';

const auth = new GoTrue({
  APIUrl: "https://MY_URL/.netlify/identity",
  setCookie: true,
});

export default auth;

My Navbar component has the line import auth from '../../utils/auth'; and in its render() function I have the line console.log(auth.currentUser());. Right after a successful login, I see the user object printed correctly into the console, but as soon as I refresh it shows undefined.

When I push my site to Netlify, the build fails with the following error in the Deploy Log:

212 |       var json = localStorage.getItem(storageKey);
...

WebpackError: localStorage is not defined

- user.js?1d1d:212 Function.recoverSession
  ~/gotrue-js/lib/user.js?1d1d:212:1

- index.js?2889:158 GoTrue.currentUser
  ~/gotrue-js/lib/index.js?2889:158:1

...

This seems to be directly related, since reviving an existing session is where GoTrue seems to be having trouble.

Thanks for your help!

Facing the same error in vanilla JS.

The localStorage is not defined error probably has to do with your call to auth.currentUser()) in your render function, because the environment for static prerendering isn't a full DOM environment. When making calls to DOM apis in a render function that is getting server side rendered, this is a caveat to look out for. There is no single answer this problem and is generally requires a solution specific to the app's use cases. For one, auth.currentUser()) will will never return a user when doing a generic static build of a site, because how could a specific user be logged-in in on the build bot. It also just so happens that the implementation of that call is accessing a DOM api that doesn't exist in the pre-render environment.

There are things we possibly do to improve this behavior by default, but I need more specific use cases to reason about this.

As far as not remaining logged in upon refresh, that sounds odd. Are you clearing local storage on app refresh? iirc the session and refresh tokens are stored in there. I'm unfamiliar with the setCookie implementation and will have to investigate further there.

If you could throw together a minimal / runnable gist that I could play with locally and recreate the issue, that would be incredibly helpful to get this solved as I don't have any specific ideas from the provided description.

@bcomnes Thanks for such a detailed response, it makes total sense. I'm not sure where it leaves me, given that somehow the Netlify Identity Widget seems to work so I feel like there must be a way to conditionally show a sign out button only when there's a logged-in user.

I'll whip up a barebones repo with Gatsby and GoTrue and pop the link in here!

Test repo at: https://github.com/tatwater/gatsby-gotrue

Temporary test creds: tatwater@outlook.com & testpass

Thanks! I'll try to take a peek before the end of the week.

FWIW, you can find a vanilla JS example here.
Test credentials: narayanaditya95@gmail.com $ aditya

wendo commented

In order to resolve gatsby builds failing, check for window when calling auth.currentUser().

const user = typeof window !== 'undefined' && auth.currentUser();

Something like above is handy as you can refer to user without needing do the check each time.

I thought about this some more, and I agree, we are leaking implementation details where we don't need to. I'm interested in fixing the implementation to behave correctly in node/non-browser environments.

This is working just fine for me.

Must set the reminder to true on login (not referenced in the doc).

import React, { Component } from "react"
import GoTrue from "gotrue-js"

// Instantiate the GoTrue auth client with an optional configuration
let auth = new GoTrue({
  APIUrl: "https://YOURS/.netlify/identity",
  audience: "",
  setCookie: true
})

export default class extends Component {
  handleLogin = () => {
    auth
      .login("demo@demo.me", "demo", true)
      .then(response => {
        alert("Success!")
        console.log(JSON.stringify({ response }))
      })
      .catch(error => {
        alert("Failed...")
        console.log(JSON.stringify({ response }))
      })
  }
  render() {
    let user = auth.currentUser()
    console.log(user)
    return (
      <>
        <button onClick={this.handleLogin}>Login</button>
      </>
    )
  }
}

But I'm a bit confused about how this API handles it...

If login reminder is set to false the user would not be saved for the session.
If cookie is set to true and login reminder is set to true the user would be stored in a cookie.
If cookie is set to false and login reminder is set to true the user would be stored in a sessionStorage.

I've made the fix, but the doc need to be updated.

I don't have bandwidth right now to address this, unassigning.

Rezi commented

@TomPichaud 's advice with reminder flag worked for me for a Sapper app. Unfortunately the docs are still not updated year after and there are many pull requests pending. Shame on netlify for such a poor support for one of its business products.