Currently when I create my routes, I check an Auth0 method - isAuthenticated() - to determine whether or not to return a protected page or redirect to login. However, this state only exists in memory and does not keep a user on their page upon browser refresh and I would like to do so.
This is a React/RR4/React Context app and my Auth0 methods are listed in Auth.js (below).
It is highly inadvisable to store login state in localStorage. And if I store my Auth0 tokens in cookies, I'm not sure how I would validate the tokens since there is no server validation set up. What is the correct condition to check that will enable secure data persistence?
ProtectedRoutes.jsx:
<Route
exact
key={route.path}
path={route.path}
render={() => (
// CONDITION TO CHECK
context.auth.isAuthenticated()
? (
<div>
<route.component />
</div>
) : <Redirect to="/login" />
)}
/>
Auth.js (added for reference):
import auth0 from 'auth0-js';
import authConfig from './auth0-variables';
class Auth {
accessToken;
idToken;
expiresAt;
tokenRenewalTimeout;
auth0 = new auth0.WebAuth({
domain: authConfig.domain,
clientID: authConfig.clientId,
redirectUri: authConfig.callbackUrl,
responseType: 'token id_token',
scope: 'openid'
});
constructor() {
this.scheduleRenewal();
this.login = this.login.bind(this);
this.logout = this.logout.bind(this);
this.handleAuthentication = this.handleAuthentication.bind(this);
this.isAuthenticated = this.isAuthenticated.bind(this);
this.getAccessToken = this.getAccessToken.bind(this);
this.getIdToken = this.getIdToken.bind(this);
this.renewSession = this.renewSession.bind(this);
this.scheduleRenewal = this.scheduleRenewal.bind(this);
}
login() {
console.log('logging in!');
this.auth0.authorize();
}
handleAuthentication() {
return new Promise((resolve, reject) => {
this.auth0.parseHash((err, authResult) => {
if (err) return reject(err);
console.log(authResult);
if (!authResult || !authResult.idToken) {
return reject(err);
}
this.setSession(authResult);
resolve();
});
});
}
getAccessToken() {
return this.accessToken;
}
getIdToken() {
return this.idToken;
}
getExpiration() {
return new Date(this.expiresAt);
}
isAuthenticated() {
let expiresAt = this.expiresAt;
return new Date().getTime() < expiresAt;
}
setSession(authResult) {
localStorage.setItem('isLoggedIn', 'true');
let expiresAt = (authResult.expiresIn * 1000) + new Date().getTime();
this.accessToken = authResult.accessToken;
this.idToken = authResult.idToken;
this.expiresAt = expiresAt;
this.scheduleRenewal();
}
renewSession() {
this.auth0.checkSession({}, (err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.setSession(authResult);
} else if (err) {
this.logout();
console.log(`Could not get a new token. (${err.error}: ${err.error_description})`);
}
});
}
scheduleRenewal() {
let expiresAt = this.expiresAt;
const timeout = expiresAt - Date.now();
if (timeout > 0) {
this.tokenRenewalTimeout = setTimeout(() => {
this.renewSession();
}, timeout);
}
}
logout() {
this.accessToken = null;
this.idToken = null;
this.expiresAt = 0;
localStorage.removeItem('isLoggedIn');
clearTimeout(this.tokenRenewalTimeout);
console.log('logged out!');
}
}
export default Auth;
One way to integrate Auth0 with your React app is to wrap your root component with an Auth0Provider that you can import from the SDK. import React from "react"; import ReactDOM from "react-dom"; import App from "./App"; import { Auth0Provider } from "@auth0/auth0-react"; ReactDOM.
There are two main things your React application needs to do to sign on a user: Get an access token from an authentication server. Send the access token to your backend server with each subsequent request.
You can use Silent authentication to renew the tokens on browser refresh.
Specifically for your react SPA app
tokenRenewed
to false
in your main App componentrenewToken
method in your auth.js
so call that in componentDidMount
methodcomponentDidMount() {
this.auth.renewToken(() => {
this.setState({tokenRenewed : true});
})
}
renewToken
to accept a callback cb
like belowrenewSession(cb) {
this.auth0.checkSession({}, (err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.setSession(authResult);
} else if (err) {
this.logout();
console.log(`Could not get a new token. (${err.error}: ${err.error_description})`);
}
if(cb) cb(err, authResult);
});
}
tokenRenewed
is true
i.e. unless you have the valid tokens renewed via silent authenticationrender() {
if(!this.state.tokenRenewed) return "loading...";
return (
// Your App component
);
}
Notes:
Allowed Web Origins
set in the Application settings for this to workIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With