Je travaille sur une application où j'utilise React comme front-end et React-apollo-graphql
pour mon appel API.
J'utilise react-hooks
c'est-à-dire dans React 16.8 +.
Ce que je fais
J'ai créé un auth.js
fichier dans lequel je stocke mes valeurs lorsque l'utilisateur se connecte et vérifie également que le jeton est valide ou non, (expiration que je vérifie), mais ce fichier ne charge que mon Je rafraîchis ou recharge la page, ce n'est pas comment ça devrait fonctionner
Mon fichier auth.js
const initialstate = {
user: null,
};
if (localStorage.getItem("JWT_Token")) {
const jwt_Token_decoded = Jwt_Decode(localStorage.getItem("JWT_Token"));
console.log(jwt_Token_decoded.exp * 1000);
console.log(Date.now());
if (jwt_Token_decoded.exp * 1000 < Date.now()) {
localStorage.clear(); // this runs only when I refresh the page or reload on route change it dosent work
} else {
initialstate.user = jwt_Token_decoded;
}
}
const AuthContext = createContext({
user: null,
login: (userData) => {},
logout: () => {},
});
const AuthReducer = (state, action) => {
switch (action.type) {
case "LOGIN":
return {
...state,
user: action.payload,
};
case "LOGOUT":
return {
...state,
user: null,
};
default:
return state;
}
};
const AuthProvider = (props) => {
const [state, dispatch] = useReducer(AuthReducer, initialstate);
const login = (userData) => {
localStorage.setItem("JWT_Token", userData.token);
dispatch({
type: "LOGIN",
payload: userData,
});
};
const logout = () => {
localStorage.clear();
dispatch({ action: "LOGOUT" });
};
return (
<AuthContext.Provider
value={{ user: state.user, login, logout }}
{...props}
/>
);
};
export { AuthContext, AuthProvider };
Comme je l'ai commenté, la ligne où je vérifie l'expiration du jeton.
Mon seul problème est de savoir pourquoi cela fonctionne sur le rechargement de la page et non sur chaque route comme nous le faisons dans le fichier de stockage lorsque nous utilisons Redux.
Mon App.js
<AuthProvider>
<Router>
<div className="App wrapper">
<Routes/>
</div>
</Router>
</AuthProvider>
Mon index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import ApolloClient from 'apollo-boost'
import { ApolloProvider } from '@apollo/react-hooks';
import { InMemoryCache } from 'apollo-cache-inmemory';
const client = new ApolloClient({
uri: 'my url',
cache: new InMemoryCache(),
});
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('root')
);
Points importants
Comme j'utilise react-apollo-graphql
alors fournissent-ils un flux d'authentification ant? comme le fait redux, nous devons créer un fichier de stockage qui stockera nos données
J'utilise React 16.8 + donc j'utilise des react-hooks donc ici j'utilise use Reducer
à partir de cela seulement.
Ma seule question est est-ce que je le fais correctement? Je suis ouvert à d'autres approches.
J'ai fait l'authentification et l'autorisation dans Vue en utilisant Vuex là-bas, j'utilise pour créer un fichier de magasin qui s'exécute sur chaque route
Même chose que j'ai fait avec Redux, dans mon fichier de magasin, j'utilise pour stocker les états et tout.
Maintenant, si j'utilise react-hooks et react-apollo-graphql donc pas besoin de faire cela avec redux.
J'utilise apollo-link-context
pour passer l'en-tête (Authorization) comme ci-dessous
const authLink = setContext(() => {
const token = localStorage.getItem('JWT_Token')
return {
headers:{
Authorization: token ? `${token}` : ''
}
}
});
Je pense qu'ici je peux vérifier sur chaque route ou sur chaque demande si le jeton est valide ou non (vérifier le temps d'expiration) s'il n'est pas valide, je vais me déconnecter et effacer mon stockage local, Effacer le stockage n'est pas un gros problème l'essentiel est comment rediriger vers la page de connexion.
Le problème auquel vous faites face est simple. Votre AuthReducer n'accepte initialState qu'une seule fois lors de sa création. Désormais, lorsque vous rechargez votre application, tout est à nouveau initialisé et l'expiration est prise en charge par votre logique. Cependant, lors du changement de route, il ne réévalue pas votre état initial.
Cependant, ce que vous pouvez faire, c'est en utilisant setContext
, vous pouvez vérifier la validation de l'expiration en décodant le jeton en utilisant jwtDecode
et actualiser le jeton s'il a expiré et enregistrer dans localStorage puisque ceci est exécuté à chaque demande
const authLink = setContext(async () => {
let token = localStorage.getItem('JWT_Token')
const { exp } = jwtDecode(token)
// Refresh the token a minute early to avoid latency issues
const expirationTime = (exp * 1000) - 60000
if (Date.now() >= expirationTime) {
token = await refreshToken()
// set LocalStorage here based on response;
}
return {
// you can set your headers directly here based on the new token/old token
headers: {
...
}
}
})
Cependant, puisque vous souhaitez rediriger vers la page de connexion et ne pas actualiser le jeton lorsque le jeton a expiré, vous pouvez utiliser un objet d'historique personnalisé avec Routes
src/history.js
import { createBrowserHistory } from 'history';
const history = createBrowserHistory()
export default history;
App.js
import history from '/path/to/history.js';
import { Router } from 'react-router-dom';
<AuthProvider>
<Router history={history}>
<div className="App wrapper">
<Routes/>
</div>
</Router>
</AuthProvider>
puis dans setContext vous pourriez faire
import history from '/path/to/history';
const authLink = setContext(async () => {
let token = localStorage.getItem('JWT_Token')
const { exp } = jwtDecode(token)
const expirationTime = (exp * 1000) - 60000
if (Date.now() >= expirationTime) {
localStorage.clear();
history.Push('/login');
}
return {
// you can set your headers directly here based on the old token
headers: {
...
}
}
})
Pour votre problème, la solution pourrait être comme:
react-router
s'est abonné pour vérifier l'état d'authentification de l'utilisateur.main
.authverify.component.js
import { withRouter } from "react-router-dom";
const AuthVerifyComponent = ({ history }) => {
history.listen(() => { // <--- Here you subscribe to the route change
if (localStorage.getItem("JWT_Token")) {
const jwt_Token_decoded = Jwt_Decode(localStorage.getItem("JWT_Token"));
console.log(jwt_Token_decoded.exp * 1000);
console.log(Date.now());
if (jwt_Token_decoded.exp * 1000 < Date.now()) {
localStorage.clear();
} else {
initialstate.user = jwt_Token_decoded;
}
}
});
return <div></div>;
};
export default withRouter(AuthVerifyComponent);
app.js
<AuthProvider>
<Router>
<div className="App wrapper">
<Routes />
<AuthVerifyComponent />
</div>
</Router>
</AuthProvider>;