javascript - Keycloak | Cannot await on updateToken() in async function - Stack Overflow

We are developing Spring application with ReactRedux frontend. We successfully integrated it with Keyc

We are developing Spring application with React/Redux frontend. We successfully integrated it with Keycloak authentication service. However, we encountered unwanted behaviour after access token timed out. Our restMiddleware looks like this (simplified):

    function restMiddleware() {
    return (next) => async (action) => {
       try{

            await keycloak.updateToken(5);

            res = await fetch(restCall.url, {
                ...restCall.options, ...{
                    credentials: 'same-origin',
                    headers: {
                        Authorization: 'Bearer ' + keycloak.token
                    }
                }
            });

       }catch(e){}
    }

The problem is, that after token expires and updateToken() is executed, async function does not stop and invokes fetch() immediately, before new access token is received. This of course prevents fetch request from succeeding and causes response with code 401. updateToken() returns a Promise, so I see no reason why await would not work on it, which certainly is happening.

I confirmed that function in updateToken().success(*function*) will execute after successful token refresh and placing fetch() inside it would solve the problem, but because of our middleware construction I cannot do that. I developed this workaround:

    function refreshToken(minValidity) {

        return new Promise((resolve, reject) => {

            keycloak.updateToken(minValidity).success(function () {
                resolve()
            }).error(function () {
                reject()
            });
        });
    }

    function restMiddleware() {
    return (next) => async (action) => {
       try{
            await refreshToken(5);

            res = await fetch(restCall.url, {
                ...restCall.options, ...{
                    credentials: 'same-origin',
                    headers: {
                        Authorization: 'Bearer ' + keycloak.token
                    }
                }
            });

       }catch(e){}
    }

And it works, but it is not elegant.

The question is: why the first solution didn't worked? Why I cannot await on updateToken() and have to use updateToken().success() instead?

I suspect this might be a bug and to confirm this is the main purpose of this question.

We are developing Spring application with React/Redux frontend. We successfully integrated it with Keycloak authentication service. However, we encountered unwanted behaviour after access token timed out. Our restMiddleware looks like this (simplified):

    function restMiddleware() {
    return (next) => async (action) => {
       try{

            await keycloak.updateToken(5);

            res = await fetch(restCall.url, {
                ...restCall.options, ...{
                    credentials: 'same-origin',
                    headers: {
                        Authorization: 'Bearer ' + keycloak.token
                    }
                }
            });

       }catch(e){}
    }

The problem is, that after token expires and updateToken() is executed, async function does not stop and invokes fetch() immediately, before new access token is received. This of course prevents fetch request from succeeding and causes response with code 401. updateToken() returns a Promise, so I see no reason why await would not work on it, which certainly is happening.

I confirmed that function in updateToken().success(*function*) will execute after successful token refresh and placing fetch() inside it would solve the problem, but because of our middleware construction I cannot do that. I developed this workaround:

    function refreshToken(minValidity) {

        return new Promise((resolve, reject) => {

            keycloak.updateToken(minValidity).success(function () {
                resolve()
            }).error(function () {
                reject()
            });
        });
    }

    function restMiddleware() {
    return (next) => async (action) => {
       try{
            await refreshToken(5);

            res = await fetch(restCall.url, {
                ...restCall.options, ...{
                    credentials: 'same-origin',
                    headers: {
                        Authorization: 'Bearer ' + keycloak.token
                    }
                }
            });

       }catch(e){}
    }

And it works, but it is not elegant.

The question is: why the first solution didn't worked? Why I cannot await on updateToken() and have to use updateToken().success() instead?

I suspect this might be a bug and to confirm this is the main purpose of this question.

Share Improve this question asked Aug 10, 2017 at 10:08 BJanuszBJanusz 631 silver badge4 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 4

Yes, as said, keycloak.js uses a homegrown promise implementation that is inpatible to the ES6 Promise. What I tend to do in my projetcs is a little helper like this one here (TypeScript).

export const keycloakPromise = <TSuccess>(keycloakPromise: KeycloakPromise<TSuccess, any>) => new Promise<TSuccess>((resolve, reject) =>
    keycloakPromise
        .success((result: TSuccess) => resolve(result))
        .error((e: any) => reject(e))
);

and use it like

keycloakPromise<KeycloakProfile>(keycloak.loadUserProfile())
    .then(userProfile => ...)
    .catch(() => ...)

updateToken method's documentation states that it returns a Promise, but it actually isn't a thenable and thus the await keyword does not think it as a valid Promise. This is how I would put it. :)

Your solution seems elegant to me and I have done the exact same thing also to correctly continue on a Promise chain after calling the updateToken function.

Keycloak JavaScript adapter documentation: https://keycloak.gitbooks.io/documentation/securing_apps/topics/oidc/javascript-adapter.html

The promiseType option upon initialization of the keycloak client defaults to legacy.

If you change it to native then you should get an ES6 promise after the refreshToken invocation

    keycloak
      .init({
        onLoad: 'login-required',
        promiseType: 'native',
      })
      .then(() => {//...})
      .catch((err) => {//...})

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1743740484a4499049.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信