React Hooks Auth0-js Context Hook没有更新

时间:2019-07-19 17:41:53

标签: reactjs auth0 react-hooks react-context

出于任何原因,有时用户在身份验证时会更新,而有时则不会。也许这与我的连接速度有关,但是我不知道为什么当我登录用户时需要刷新页面以使用户出现。我认为我使用钩子的方式不正确,因此感谢所有帮助。如何让用户登录/注册时实时更新而不是刷新页面。

这是我的身份验证文件:

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')
  }
}

我的应用文件:

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;

回调页面:

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

2 个答案:

答案 0 :(得分:2)

对于这样的问题,重要的第一步是确保道具按预期放倒。另一种选择是在进行工作流时捕获HAR文件,以验证一切均按预期传播。虽然这不能解决您的问题,但可以帮助您完成任务。

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

答案 1 :(得分:0)

这花了我几天时间才能弄清楚,但感谢莫里森为我指明了正确的方向。问题在于,挂钩没有更新,因为我只是将值传递给上下文而不是动态值。这就是为什么我刷新时会更新的原因。

关键是将Auth0Context.Provider从App.js内部移动到它自己的文件,该文件还包含所有其他功能以及用户和已验证的实际状态。希望这可以帮助其他人,但是基本上我需要使用钩子来确保上下文在钩子中正在更改。

 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;

新的app.js文件:

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;