I have a SPA react application where I am using auth0 for authentication. I would like to do a silent authentication and get a new token whenever site is refreshed, like it is suggested in this answer. I have an Auth class responsible for handling tokens:
import auth0 from 'auth0-js'
import { authConfig } from '../config'
export default class Auth {
accessToken
idToken
expiresAt
auth0 = new auth0.WebAuth({
domain: authConfig.domain,
clientID: authConfig.clientId,
redirectUri: authConfig.callbackUrl,
responseType: 'token id_token',
scope: 'openid'
})
constructor(history) {
this.history = history
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)
}
login() {
this.auth0.authorize()
}
handleAuthentication() {
this.auth0.parseHash((err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
console.log('Access token: ', authResult.accessToken)
console.log('id token: ', authResult.idToken)
this.setSession(authResult)
} else if (err) {
this.history.replace('/')
console.log(err)
alert(`Error: ${err.error}. Check the console for further details.`)
}
})
}
getAccessToken() {
return this.accessToken
}
getIdToken() {
return this.idToken
}
setSession(authResult) {
// Set isLoggedIn flag in localStorage
localStorage.setItem('isLoggedIn', 'true')
// Set the time that the access token will expire at
let expiresAt = authResult.expiresIn * 1000 + new Date().getTime()
this.accessToken = authResult.accessToken
this.idToken = authResult.idToken
this.expiresAt = expiresAt
// navigate to the home route
this.history.replace('/')
}
renewSession(cb) {
this.auth0.checkSession({}, (err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.setSession(authResult)
cb(err, authResult)
} else if (err) {
this.logout()
console.log(
`Could not get a new token (${err.error}: ${err.error_description}).`
)
}
})
}
logout() {
// Remove tokens and expiry time
this.accessToken = null
this.idToken = null
this.expiresAt = 0
// Remove isLoggedIn flag from localStorage
localStorage.removeItem('isLoggedIn')
this.auth0.logout({
return_to: `${window.location.origin}/login`
})
}
isAuthenticated() {
// Check whether the current time is past the
// access token's expiry time
let expiresAt = this.expiresAt
return new Date().getTime() < expiresAt
}
}
In my main App component in componentDidMount I am calling the renewSession method from Auth class:
export default class App extends Component<AppProps, AppState> {
constructor(props: AppProps) {
super(props)
this.handleLogin = this.handleLogin.bind(this)
this.handleLogout = this.handleLogout.bind(this)
this.createNewPost = this.createNewPost.bind(this)
}
state: AppState = {
tokenRenewed: false
}
componentDidMount() {
this.props.auth.renewSession(() => {
this.setState({ tokenRenewed: true })
})
}
handleLogin() {
this.props.auth.login()
}
handleLogout() {
this.props.auth.logout()
}
async createNewPost() {
const idToken = this.props.auth.getIdToken()
try {
const newPost = await createPost(idToken)
this.props.history.push(`/posts/${newPost.postId}/edit`)
} catch {
alert('Post creation failed')
}
}
render() {
if (!this.state.tokenRenewed) return 'loading...'
const userAuthenticated = this.props.auth.isAuthenticated()
return (
<div>
<Segment vertical>
<Grid container stackable verticalAlign="middle">
<Grid.Row>
<Grid.Column width={16}>
<Router history={this.props.history}>
{this.generateMenu(userAuthenticated)}
{this.generateCurrentPage(userAuthenticated)}
</Router>
</Grid.Column>
</Grid.Row>
</Grid>
</Segment>
</div>
)
}
And this are my routes:
<Router history={history}>
<Switch>
<Route
path="/callback"
render={props => {
handleAuthentication(props)
return <Callback />
}}
/>
<Route
path="/login"
render={props => {
return <LogIn auth={auth} {...props} />
}}
/>
<Route
path="/"
render={props => {
return <App auth={auth} {...props} />
}}
/>
</Switch>
</Router>
That works fine if I login with username/password. But, if I use a social login like Google/Gmail then whenever I login in to the app, I get an error from auth0.checkSession in Auth class:
Could not get a new token (login_required: Login required).
How can I make this work with Google/Gmail login as well?