I am trying to get Nuxt Auth to work with JWT_Sessions Ruby gem as it's shown here: the /login entrypoint sends back an access token as a non-HttpOnly cookie and a CSRF token in the response body. When the token expires you can send both these tokens to a /refresh endpoint to get a fresh pair.
nuxt.config.js
auth: {
plugins: [ '~/plugins/auth.js', '~/plugins/axios' ],
strategies: {
jwtSessions: {
_scheme: '~/plugins/auth-jwt-sessions.js',
endpoints: {
login: { url: '/login', method: 'post', propertyName: false },
logout: { url: '/logout', method: 'post' },
user: { url: '/me', method: 'get', propertyName: false }
},
tokenRequired: false,
tokenType: false
}
},
~/plugins/auth-jwt-sessions.js
...
async login (endpoint) {
// Ditch any leftover local tokens before attempting to log in
await this._logoutLocally()
const result = await this.$auth.request(
endpoint,
this.options.endpoints.login
)
if (result.csrf){
this.$auth.$storage.setUniversal("csrf_token", result.csrf)
console.log("sucessfully set up CSRF token " + this.$auth.$storage.getUniversal("csrf_token"));
}
return this.fetchUser()
}
...
~/plugins/axios.js
export default function ({$auth, $axios, redirect}) {
$axios.onRequest(config => {
var csrf = $auth.$storage.getUniversal('csrf_token')
if ($auth.$state.loggedIn && csrf){
config.headers['X-CSRF-TOKEN'] = csrf
}
})
$axios.onResponseError(async error => {
var response = error.response
if($auth.$state.loggedIn && response.status === 401 && response.request._retry != true) {
const originalRequest = response.config
originalRequest._retry = true
const newToken = await $axios.post('/refresh').then(response => {
console.log(response);
return response.data.csrf
}).catch(response => {
console.log(response);
})
if(newToken) {
$auth.$storage.setUniversal('csrf_token', newToken)
originalRequest.headers['X-CSRF-TOKEN'] = newToken
return $axios(originalRequest)
} else {
redirect(301, '/login')
}
} else {
return Promise.reject(error)
}
});
}
The above code works alright until you need to reload/reopen a page after the access token has expired.
Since for some reason my $axios interceptors don't work during strategy initialization (btw, why?), I had to add a refreshing routine to the strategy's mounted function on the client as shown below.
~/plugins/auth-jwt-sessions.js
...
mounted () {
if (this.options.tokenRequired) {
const token = this.$auth.syncToken(this.name)
this._setToken(token)
}
this.$auth.$storage.syncUniversal('csrf_token')
if (process.client) {
let csrf = this.$auth.$storage.getUniversal('csrf_token')
if (csrf) {
console.log("recovering CSRF token from Universal Storage");
this.$auth.ctx.app.$axios.setHeader('X-CSRF-TOKEN', csrf)
var newToken = null
this.$auth.ctx.app.$axios.post('/refresh').then(response => {
console.log(response.data);
newToken = response.data
this.$auth.$storage.setUniversal('csrf_token', newToken.csrf)
this.$auth.ctx.app.$axios.get('/me', {}, {
headers: {'X-CSRF-TOKEN': newToken.csrf},
withCredentials: false
}).then(response => {
console.log(this.$auth.$state);
this.$auth.setUser(response.data)
}).catch(response => {
console.log(response);
})
})
}
}
return this.$auth.fetchUserOnce()
}
...
async fetchUser (endpoint) {
// Token is required but not available
if (this.options.tokenRequired && !this.$auth.getToken(this.name)) {
return
}
// the headers need to be configured here once again, the axios config doesn't get persisted
this.$auth.ctx.app.$axios.setHeader('X-CSRF-TOKEN', this.$auth.$storage.getUniversal("csrf_token"))
// User endpoint is disabled.
if (!this.options.endpoints.user) {
this.$auth.setUser({})
return
}
// Try to fetch user and then set
const user = await this.$auth.requestWith(
this.name,
endpoint,
this.options.endpoints.user
)
this.$auth.setUser(user)
}
...
It seems to be working, but when I let my access token expire and then reload the application, I get redirected to /login for a few moments, and then the session gets restored and I get redirected to the initial route.
1) Is this the right way to achieve cookie-based refresh flow in Nuxt?
2) How can I avoid multiple redirects while the session is being restored during mounted()?