钥匙斗篷 |无法在异步函数中等待 updateToken()
Keycloak | Cannot await on updateToken() in async function
我们正在开发 Spring 具有 React/Redux 前端的应用程序。我们成功地将它与 Keycloak 身份验证服务集成在一起。但是,我们在访问令牌超时后遇到了不良行为。我们的 restMiddleware 看起来像这样(简化):
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){}
}
问题是,在令牌过期并执行 updateToken() 之后,异步函数不会停止并在收到新访问令牌之前立即调用 fetch()。这当然会阻止获取请求成功并导致代码 401 的响应。updateToken() returns 一个承诺,所以我看不出为什么 await 不能处理它,这肯定正在发生。
我确认 updateToken().success(*function*)
中的函数将在令牌刷新成功后执行并将 fetch() 放入其中可以解决问题,但由于我们的中间件构造,我无法做到这一点。我开发了这个解决方法:
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){}
}
它可以工作,但不够优雅。
问题是:为什么第一个解决方案不起作用?为什么我不能等待 updateToken() 而必须改用 updateToken().success()?
我怀疑这可能是一个错误,确认这是这个问题的主要目的。
updateToken
方法的文档指出它 returns 是 Promise
,但它实际上不是 then
可用的,因此 await
关键字可以不要认为它是有效的 Promise
。我就是这样说的。 :)
你的解决方案对我来说似乎很优雅,我也做了完全相同的事情,以便在调用 updateToken
函数后正确地在 Promise
链上继续。
Keycloak JavaScript 适配器文档:https://keycloak.gitbooks.io/documentation/securing_apps/topics/oidc/javascript-adapter.html
是的,如前所述,keycloak.js 使用与 ES6 Promise 不兼容的自主开发的 promise 实现。我倾向于在我的项目中做的是像这里这样的小帮手 (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))
);
并像
一样使用它
keycloakPromise<KeycloakProfile>(keycloak.loadUserProfile())
.then(userProfile => ...)
.catch(() => ...)
keycloak客户端初始化时的promiseType
选项默认为legacy
。
如果你把它改成 native
那么你应该在
refreshToken
调用
keycloak
.init({
onLoad: 'login-required',
promiseType: 'native',
})
.then(() => {//...})
.catch((err) => {//...})
我们正在开发 Spring 具有 React/Redux 前端的应用程序。我们成功地将它与 Keycloak 身份验证服务集成在一起。但是,我们在访问令牌超时后遇到了不良行为。我们的 restMiddleware 看起来像这样(简化):
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){}
}
问题是,在令牌过期并执行 updateToken() 之后,异步函数不会停止并在收到新访问令牌之前立即调用 fetch()。这当然会阻止获取请求成功并导致代码 401 的响应。updateToken() returns 一个承诺,所以我看不出为什么 await 不能处理它,这肯定正在发生。
我确认 updateToken().success(*function*)
中的函数将在令牌刷新成功后执行并将 fetch() 放入其中可以解决问题,但由于我们的中间件构造,我无法做到这一点。我开发了这个解决方法:
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){}
}
它可以工作,但不够优雅。
问题是:为什么第一个解决方案不起作用?为什么我不能等待 updateToken() 而必须改用 updateToken().success()?
我怀疑这可能是一个错误,确认这是这个问题的主要目的。
updateToken
方法的文档指出它 returns 是 Promise
,但它实际上不是 then
可用的,因此 await
关键字可以不要认为它是有效的 Promise
。我就是这样说的。 :)
你的解决方案对我来说似乎很优雅,我也做了完全相同的事情,以便在调用 updateToken
函数后正确地在 Promise
链上继续。
Keycloak JavaScript 适配器文档:https://keycloak.gitbooks.io/documentation/securing_apps/topics/oidc/javascript-adapter.html
是的,如前所述,keycloak.js 使用与 ES6 Promise 不兼容的自主开发的 promise 实现。我倾向于在我的项目中做的是像这里这样的小帮手 (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))
);
并像
一样使用它keycloakPromise<KeycloakProfile>(keycloak.loadUserProfile())
.then(userProfile => ...)
.catch(() => ...)
keycloak客户端初始化时的promiseType
选项默认为legacy
。
如果你把它改成 native
那么你应该在
refreshToken
调用
keycloak
.init({
onLoad: 'login-required',
promiseType: 'native',
})
.then(() => {//...})
.catch((err) => {//...})