I am using cognito for user authentication. Upon successful authentication (userEmail and password) Cognito generates id, access and refresh tokens which I can see in my console.log
responses.
My question is, how can extract these tokens and store them as a 'global scope variable' for use (potentially as a HTTPOnly
Cookie) with API calls in various React components in my application?
(I am open to any suggested 'fixes' to my current approach, or equally, sugestions on more efficient ways of managing this): What I have tried..
Defining the JWT token as a variable in a JWTutils.js
component and importing it to the login.js
where I assign the value of the response from cognito. My thinking is that this would allow me to use the token value throughout the application. All, this does however, is interrupt a successful login. So even when I enter correct details, my console.log
actually shows onSuccess
, but immediately after my browser renders the cognito Modal saying Failure : Username or password are incorrect
.
What is the best way to deal with and manage the JWT from the client side?
My code is attached below: Login.js
import React from 'react'
import poolData from '../Userpool';
import { CognitoUser, AuthenticationDetails } from 'amazon-cognito-identity-js';
import { useState, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import JWT from '../utils/JWTutils';
const Login = () => {
const[email,setEmail]=useState("");
const[password,setPassword]=useState("");
let isAuth = false;
let history = useHistory();
const onSubmit =(event)=>{
event.preventDefault();
const user= new CognitoUser({
Username: email,
Pool: poolData,
} );
const authDetails = new AuthenticationDetails({
Username: email,
Password : password,
} );
console.log("authDetails", authDetails)
console.log("user", user )
user.authenticateUser(authDetails,{
onSuccess: (data)=>{
console.log("On Success: " , data)
console.log("JWT", data.idToken.jwtToken)
JWT=data.idToken.jwtToken
isAuth =true;
JWT = (data.idToken.jwtToken) // <----this is where the modal 'pops up' and the //application fails
history.push("/secure");
} ,
onFailure:(err)=>{
alert("Failure : Username or password are incorrect")
history.push("/")
} ,
newPasswordRequired: (data)=>{
console.log("New Password Required: ", data)
}
} )
}
return (
<div>
<br />
<hr />
<h2>Login</h2>
<form onSubmit={onSubmit} >
<label htmlFor="email">Email</label>
<br />
<input type="email" value={email}
onChange={(event)=>setEmail(event.target.value)} />
<br />
<label htmlFor="password">Password</label>
<br />
<input type="password" value={password}
onChange={(event=>setPassword(event.target.value))} />
<br />
<button>Login</button>
</form>
</div>
)
}
export default Login;
JWTutils.js
const JWT =""
export default JWT;
The way you have it, JWT
is a const
and cannot be reassigned. This is likely why your current approach blows up.
You could easily convert JWTUtils to a simple "store" that allows getting/setting of the token:
// JWTUtils
let token;
export default {
setToken: t => token = t,
getToken: () => token,
}
And then use that in your success
handler:
onSuccess: (data) => {
JWT.setToken(data.idToken.jwtToken)
// ...whatever else you need to do...
}
Anything else that needs it can then access it via getToken:
import JWT from '../JWTUtils';
const token = JWT.getToken();
if (token) {
// do whatever you need to do
}
You could extend the JWTUtil to stash it in local storage so it would persist across page reloads, etc.
Note: You could just change const JWT = ""
to let JWT = ""
, but this would allow any code anywhere to change its value, which is A Bad Idea™ and could lead to problems that are difficult to diagnose.
By sending it through a well-defined API (getToken, setToken) and keeping the variable itself private you can enforce rules around how it's accessed, set debugging breakpoints whenever it changes, swap out the actual implementation without affecting existing usage, etc.