Problem:
Every time when I log in, the id token which is obtained by Auth.signIn will be store in localStorage.
After I login, UI make requests which require Authorization(use id token),
but it failed every time.
I tried to copy the id token in localStorage and tried the same API request in Postman,
below error message shown.
the incoming token has expired
But When I reload the page, the request is sent successfully and receive ok response.
I am not sure whether it's because the token refreshing logic is not correct in my code.
I just put the token refreshing logic in App.js componentDidMount().
The logic is based on below post.
how handle refresh token service in AWS amplify-js
Can someone let me know what's wrong of my code?
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
//aws
import Amplify from 'aws-amplify';
import config from './config.json'
const Index = () => {
Amplify.configure({
Auth: {
mandatorySignId: true,
region: config.cognito.REGION,
userPoolId: config.cognito.USER_POOL_ID,
userPoolWebClientId: config.cognito.APP_CLIENT_ID
}
});
return(
<React.StrictMode>
<App/>
</React.StrictMode>
)
}
ReactDOM.render(
<Index />,
document.getElementById('root')
);
serviceWorker.unregister();
App.js
import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom';
import { Redirect } from 'react-router';
import { withRouter } from 'react-router-dom';
import config from './config.json'
//Screen
import Login from './screen/auth/Login'
import Drawer from './components/Drawer'
import { Auth } from 'aws-amplify';
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');
const CognitoUserPool = AmazonCognitoIdentity.CognitoUserPool;
class App extends Component {
state = {
isAuthenticated: false,
isAuthenticating: true,
user: null
}
setAuthStatus = authenticated =>{
this.setState({isAuthenticated: authenticated})
}
setUser = user =>{
this.setState({ user: user})
}
handleLogout = async () =>{
try{
Auth.signOut();
this.setAuthStatus(false);
this.setUser(null)
localStorage.removeItem('jwtToken')
localStorage.removeItem('idToken')
this.props.history.push('/')
}catch(error){
console.log(error)
}
}
tokenRefresh(){
const poolData = {
UserPoolId : config.cognito.USER_POOL_ID, // Your user pool id here,
ClientId : config.cognito.APP_CLIENT_ID// Your client id here
};
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
const cognitoUser = userPool.getCurrentUser();
cognitoUser.getSession((err, session) =>{
const refresh_token = session.getRefreshToken();
cognitoUser.refreshSession(refresh_token, (refErr, refSession) => {
if (refErr) {
throw refErr;
}
else{
localStorage.setItem('jwtToken',refSession.idToken.jwtToken)
localStorage.setItem('idToken',JSON.stringify(refSession.idToken))
}
});
})
}
async componentDidMount(){
try{
const session = await Auth.currentSession();
this.setAuthStatus(true);
const user = await Auth.currentAuthenticatedUser();
this.setUser(user);
}catch(error){
console.log(error);
}
// check if the token need refresh
this.setState({isAuthenticating: false})
let getIdToken = localStorage.getItem('idToken');
if(getIdToken !== null){
let newDateTime = new Date().getTime()/1000;
const newTime = Math.trunc(newDateTime);
const splitToken = getIdToken.split(".");
const decodeToken = atob(splitToken[1]);
const tokenObj = JSON.parse(decodeToken);
const newTimeMin = ((newTime) + (5 * 60)); //adding 5min faster from current time
if(newTimeMin > tokenObj.exp){
this.tokenRefresh();
}
}
}
render(){
const authProps = {
isAuthenticated: this.state.isAuthenticated,
user: this.state.user,
setAuthStatus: this.setAuthStatus,
setUser: this.setUser
}
return (
!this.state.isAuthenticating &&
<React.Fragment>
{this.state.isAuthenticated ?
<Drawer props={this.props} auth={authProps} handleLogout={this.handleLogout} onThemeChange={this.props.onThemeChange} /> :
<Switch>
<Redirect exact from='/' to='/login'/>
<Route path='/login' render={(props)=> <Login {...props} auth={authProps}/>} />
</Switch>
}
</React.Fragment>
);
}
}
export default withRouter(App);
Login.js
import React, { useState } from 'react';
import TextField from '@material-ui/core/TextField';
import withStyles from '@material-ui/core/styles/withStyles';
import _ from 'lodash';
import { Auth } from "aws-amplify";
function Login(props) {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = async (event) => {
event.preventDefault();
const payload = {
"username": username,
"password": password
}
// aws login
try{
const signInResponse = await Auth.signIn(payload.username,payload.password)
console.log(signInResponse)
props.history.push("/home")
props.auth.setAuthStatus(true)
props.auth.setUser(signInResponse)
localStorage.setItem('jwtToken',signInResponse.signInUserSession.idToken.jwtToken)
localStorage.setItem('idToken',JSON.stringify(signInResponse.signInUserSession.idToken))
}catch(error){
console.log(error)
}
}
return(
<form onSubmit={handleSubmit}>
<TextField
name='username'
value="username"
...
/>
<TextField
name='password'
value="password"
...
/>
</form>
);
}
export default withStyles(styles)(Login);