简体   繁体   中英

React Hooks Auth0-js Context Hook Not Updating

For whatever reasons sometimes the user gets updated when authenticated and sometimes it doesn't. Perhaps this is an issue with my connection speed, but I can't figure out why when I log a user in I need to refresh the page for the user to appear. I think I am using hooks incorrectly so all help is appreciated. How do I get the user to update live when they login/ signup vs having to refresh the page.

Here is my auth file:

import auth0 from 'auth0-js';
import history from '../history';
import config from "../config/auth_config.js";

export default class Auth {

  accessToken;
  idToken;
  expiresAt;

  auth0 = new auth0.WebAuth({
    domain: config.domain,
    clientID: config.clientId,
    redirectUri: `${window.location.origin}/auth0_callback`,
    responseType: 'token id_token',
    scope: 'openid profile email',
  })

  // login method takes email password and db connection
  login = (email, password) => {
    this.auth0.authorize({
      "connection": 'Username-Password-Authentication',
      "email": email,
      "password": password,
    }, function (err) {
      console.log(err);
      if (err) return alert('Something went wrong: ' + err);
    })
  }

  // signup method takes email password and db connection
  signUp = (email, password) => {
    this.auth0.redirect.signupAndLogin({
      "connection": 'Username-Password-Authentication',
      "email": email,
      "password": password
    }, function (err) {
      if (err) return alert('Something went wrong: ' + err);
    })
  };

  // logoout method removes all id's from local storage
  logout = () => {
    localStorage.removeItem('access_token')
    localStorage.removeItem('id_token')
    localStorage.removeItem('expires_at')
    localStorage.removeItem('user')
    history.replace('/');
  }

  // method called once callback initiated
  handleAuthentication = () => {
    if (typeof window !== 'undefined') {
      this.auth0.parseHash((err, authResult) => {
        debugger;
        if (authResult && authResult.accessToken && authResult.idToken) {
          this.setSession(authResult);
        } else if (err) {
          console.log(err)
          return err;
        }
      })
    }
  }


  isAuthenticated = () => {
    if (typeof localStorage !== 'undefined') {
      const expiresAt = JSON.parse(localStorage.getItem('expires_at'))
      return new Date().getTime() < expiresAt
    } else {
      return false
    }
  }

  setSession = authResult => {
    const expiresAt = JSON.stringify(
      authResult.expiresIn * 1000 + new Date().getTime()
    )
    localStorage.setItem('access_token', authResult.accessToken)
    localStorage.setItem('id_token', authResult.idToken)
    localStorage.setItem('expires_at', expiresAt)

    this.auth0.client.userInfo(authResult.accessToken, (err, user) => {
      localStorage.setItem('user', JSON.stringify(user))
    })

    history.replace('/');
  }

  renewSession = () => {
    this.auth0.checkSession({}, (err, authResult) => {
      if (authResult && authResult.accessToken && authResult.idToken) {
        this.setSession(authResult);
      } else if (err) {
        this.logout();
        console.log(err);
        alert(
          `Could not get a new token (${err.error}: ${err.error_description}).`
        );
      }
    });
  }

  getUser = () => {
    if (localStorage.getItem('user')) {
      return JSON.parse(localStorage.getItem('user'))
    }
  }

  getUserName = () => {
    if (this.getUser()) {
      return this.getUser().name
    }
  }

  getToken = () => {
    return localStorage.getItem('id_token')
  }
}

My App file:

import React, { useContext, useEffect } from "react";
import './App.scss';

import { Router, Route, Switch } from "react-router-dom";
import history from "./history";
import Auth from "./auth/Auth";
import Home from "./scenes/Home/Home";
import SignUp from "./scenes/SignUp/SignUp";
import Auth0Callback from "./scenes/Auth0Callback/Auth0Callback";


export const Auth0Context = React.createContext();
export const useAuth0 = () => useContext(Auth0Context);

const auth = new Auth();
let authenticated = auth.isAuthenticated();
let user = auth.getUser();

const handleAuthentication = (nextState, replace) => {
  console.log(nextState);
  if (/access_token|id_token|error/.test(nextState.location.hash)) {
    auth.handleAuthentication();
    authenticated = auth.isAuthenticated();
    user = auth.getUser();
  }
}

function App() {

  // here is where I get false the first time until i hard refresh
  console.log(authenticated);  
  // I get undefined until I hard refresh
  console.log(user);

  // call as if componentDidMount to see if user is logged in
  // if so extend their session
  useEffect(() => {
    if (localStorage.getItem("isLoggedIn") === "true") {
      auth.renewSession();
    }
  }, []);


  return (
    <Auth0Context.Provider value={{ authenticated, user }}>
      <div className="App">
        <Router history={history}>
          <Switch>
            <Route path="/" exact component={Home} />
            <Route path="/signup" exact component={SignUp} />
            <Route path="/auth0_callback" render={(props) => {
              handleAuthentication(props);
              return <Auth0Callback {...props} />
            }} />
          </Switch>
        </Router>
      </div>
    </Auth0Context.Provider>
  );
}

export default App;

Callback page:

import React from 'react'
import { ClipLoader } from 'react-spinners'

import Auth from '../../auth/Auth'

const Auth0CallbackPage = () => {
  return (
    <div>
      <h1>
        This is the auth callback page
      </h1>
      <ClipLoader sizeUnit="px" size={150} />
    </div>
  )
}

export default Auth0CallbackPage

With an interment issue like this, a great first step is to make sure your props are rendering down as you expect them to. Another option is to capture a HAR file while going along your workflow to verify everything is propagated down as you expect. While this doesn't resolve your issue, it may help in your quest.

https://auth0.com/docs/troubleshoot/har

This took me a couple days to figure out but thanks morrison for pointing me in the right direction. The issue lied in that the hooks weren't updating since I was just passing in a value to context vs a dynamic value. That's why it updated when I refreshed.

The key was to move the Auth0Context.Provider from inside of the App.js to it's own file which also contained all the other functions as well as the actual state for user and authenticated. Hopefully this can help someone else out, but basically with hooks I needed to make sure that the context was changing inside the hook.

 import React, {useContext, useState} from 'react'
    import auth0 from 'auth0-js';
    import history from '../history';
    import config from "../config/auth_config.js";

    export const Auth0Context = React.createContext();
    export const useAuth0 = () => useContext(Auth0Context);

    const Auth0Provider = (props) => {

      const [authenticated, setAuthenticated] = useState();
      const [user, setUser] = useState();

      const auth0Client = new auth0.WebAuth({
        domain: config.domain,
        clientID: config.clientId,
        redirectUri: `${window.location.origin}/auth0_callback`,
        responseType: 'token id_token',
        scope: 'openid profile email',
      })

      // login method takes email password and db connection
      const login = (email, password) => {
        auth0Client.authorize({
          "connection": 'Username-Password-Authentication',
          "email": email,
          "password": password,
        }, function (err) {
          console.log(err);
          if (err) return alert('Something went wrong: ' + err);
        })
      }

      // signup method takes email password and db connection
      const signUp = (email, password) => {
        auth0Client.redirect.signupAndLogin({
          "connection": 'Username-Password-Authentication',
          "email": email,
          "password": password
        }, function (err) {
          if (err) return alert('Something went wrong: ' + err);
        })
      };

      // logoout method removes all id's from local storage
      const logout = () => {
        localStorage.removeItem('access_token')
        localStorage.removeItem('id_token')
        localStorage.removeItem('expires_at')
        localStorage.removeItem('user')
        history.replace('/');
        setAuthenticated(false);
        setUser(null);
      }

      // method called once callback initiated
      const handleAuthentication = () => {
        if (typeof window !== 'undefined') {
          auth0Client.parseHash((err, authResult) => {
            if (authResult && authResult.accessToken && authResult.idToken) {
              setSession(authResult);
            } else if (err) {
              console.log(err)
              return err;
            }
          })
        }
      }



      const isAuthenticated = () => {
        if (typeof localStorage !== 'undefined') {
          const expiresAt = JSON.parse(localStorage.getItem('expires_at'))
          setAuthenticated(true);
          return new Date().getTime() < expiresAt
        } else {
          return false
        }
      }

      const setSession = async authResult => {
        console.log(authResult);
        const expiresAt = JSON.stringify(
          authResult.expiresIn * 1000 + new Date().getTime()
        )
        localStorage.setItem('access_token', authResult.accessToken)
        localStorage.setItem('id_token', authResult.idToken)
        localStorage.setItem('expires_at', expiresAt)

        localStorage.setItem('user', authResult.idTokenPayload)
        setAuthenticated(true);
        setUser(authResult.idTokenPayload);

        history.replace('/');
      }

      const renewSession = () => {
        auth0Client.checkSession({}, (err, authResult) => {
          if (authResult && authResult.accessToken && authResult.idToken) {
            this.setSession(authResult);
          } else if (err) {
            this.logout();
            console.log(err);
            alert(
              `Could not get a new token (${err.error}: ${err.error_description}).`
            );
          }
        });
      }

      const getUser = () => {
        if (localStorage.getItem('user')) {
          return JSON.parse(localStorage.getItem('user'))
        }
      }

      const getUserName = () => {
        if (this.getUser()) {
          return this.getUser().name
        }
      }

      const getToken = () => {
        return localStorage.getItem('id_token')
      }

      return (
        <Auth0Context.Provider
          value={{
            login,
            signUp,
            logout,
            handleAuthentication,
            isAuthenticated,
            setSession,
            renewSession,
            getUser,
            getUserName,
            getToken,
            authenticated,
            user
          }}
        >
          {props.children}
        </Auth0Context.Provider>
      );
    }

    export default Auth0Provider;

new app.js file:

import Auth0Callback from "./scenes/Auth0Callback/Auth0Callback";
import { useAuth0 } from "./auth/Auth";

function App() {

  const { renewSession, handleAuthentication } = useAuth0();

  const handleAuth = (nextState, replace) => {
    if (/access_token|id_token|error/.test(nextState.location.hash)) {
      handleAuthentication();
    }
  }
  // call as if componentDidMount to see if user is logged in
  // if so extend their session
  useEffect(() => {
    if (localStorage.getItem("isLoggedIn") === "true") {
      renewSession();
    }
  }, []);


  return (
    <div className="App">
      <Router history={history}>
        <Switch>
          <Route path="/" exact component={Home} />
          <Route path="/signup" exact component={SignUp} />
          <Route path="/login" exact component={Login} />
          <Route path="/auth0_callback" render={(props) => {
            handleAuth(props);
            return <Auth0Callback {...props} />
          }} />
        </Switch>
      </Router>
    </div>
  );
}

export default App;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM