I have a problem with the management of the jwt token from Kuzzle javascript SDK's auth controller. I'm a complete beginner in JS or Kuzzle, sorry for any bad assumptions.
I use a simple admin account on my Express server with the rights to register and update users, or create indices/collections. Here is the setup so far.
const {Kuzzle, WebSocket} = require('kuzzle-sdk')
const config = require('../config/config')
const kuzzle = new Kuzzle(new WebSocket('localhost'))
// Add a listener to detect connection problems
kuzzle.on('networkError', (error) => console.error("Network Error: " + error))
// start admin session
if (!kuzzle.connected) {
kuzzle.connect()
.then(() => kuzzle.auth.login('local', {username: config.kuzzle, password: config.kuzzle.password}))
.catch((error) => {
console.error(error)
kuzzle.disconnect()
})
}
At token expiration, I get asecurity.token.invalid
error when I would expect from the security error codes a security.token.expired
. Then, all following requests fail with a security.rights.forbidden
id.
// token not expired
await kuzzle.auth.getCurrentUser()
.then((user) => console.log(user.content)) // { profileIds: [ 'admin' ], _kuzzle_info: {...} }
.catch((err) => console.error(err)) // X
// token just expired
await kuzzle.auth.getCurrentUser()
.then((user) => console.log(user.content)) // X
.catch((err) => console.error(err)) // [KuzzleError: Invalid token.] {status: 401, id: 'security.token.invalid', code: 117506049}
// following user
await kuzzle.auth.getCurrentUser()
.then((user) => console.log(user.content)) // { profileIds: [ 'anonymous' ], name: 'Anonymous' }
.catch((err) => console.error(err)) // X
// any following request (admin does have those permissions)
await kuzzle.index.exists("existing")
.catch((err) => console.error(err)) // [KuzzleError: Insufficient permissions to execute the action "index:exists".] {status: 403, id: 'security.rights.forbidden', code: 117637122}
Three questions:
Is "invalid" the expected behaviour? I think I successfully login, and Kuzzle does not throw before the expiration date, so I would expect an expired and not invalid token. I tried with 5 seconds or 2 hours tokens with same results.
Why is Kuzzle logging me out for an invalid token? Not that I am complaining, just curious.
And how do I recover from an expired token in Kuzzle? Is there a good practice to do so? Or better yet, can I automatically refresh the token for as long as the server lives, before being logged out?
My quick and dirty solution for now is to use an error handler Express middleware to intercept the error if it is an instance of kuzzle-sdk/src/KuzzleError.js, check for any of these two error ids, and re-login. I then leave my client side with the task of retrying the query, which is a bit of a pain to handle.
I am using kuzzle-sdk v7.1.4.
Many thanks!
Kuzzle developer here.
I see that you are using the Javascript SDK and so the behavior is slightly different than using directly the API.
First, when you are subscribing to realtime notification, Kuzzle checks periodically if the token associated with the subscription is still valid.
If the token have expired then Kuzzle send a TokenExpired notification and will not send any other further notification for this subscription.
The SDK receive the notification, emit the tokenExpired event and also set the SDK instance authentication token to null
since it can't use it anymore.
That's why you receive security.rights.forbidden
error in subsequent calls because you don't have any authentication token so Kuzzle give you the anonymous user rights.
When you use an expired authentication token, you should receive a security.token.expired
error. Actually there is a bug so you only receive security.token.invalid
.
To answer 2 first questions:
How to recover from an expired token
The first thing you may want to do it's prevent the token expiration in the first way.
You can use the auth:refreshToken, use auth:login with a higher expiration time or use auth:createApiKey to generate an API key that never expires.
If you have to recover from an expired token because you reach the expiration time, then you have to login again with auth:login.
kuzzle.on('tokenExpired', () => kuzzle.auth.login(...));
In your usecase (use the SDK in a backend application) I would recommend you to authenticate with an API key without expiration.
kuzzle.jwt = <api-key>
// further request will be authenticated with the API key user