javascriptnode.jsexpresssdkkuzzle

How do I recover from an expired Kuzzle token server side?


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:

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!


Solution

  • 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:

    1. Is "invalid" the expected behaviour? (for expired tokens) : No, it will be fixed soon
    2. Why is Kuzzle logging me out for an invalid token?: An invalid token cannot grant rights so Kuzzle returns an error

    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