[英]Access Denied after interactionFinished using node-oidc-provider
I'm using node-oidc-provider in a custom framework, adonisjs to be specific.我在自定义框架中使用 node-oidc-provider,具体来说是 adonisjs。
At this point, I can visit http://localhost:3333/auth?client_id=foo&redirect_uri=http://localhost:3333/launch&response_type=code&scope=openid&nonce=123&state=321
and be redirected to the /interaction/:uid page where I am asked to log in.此时,我可以访问http://localhost:3333/auth?client_id=foo&redirect_uri=http://localhost:3333/launch&response_type=code&scope=openid&nonce=123&state=321
并被重定向到 /interaction/:uid 页面,其中我被要求登录。
When I hit submit, I do call interactionFinished
as stated in the documentation.当我点击提交时,我确实按照文档中的说明调用了interactionFinished
。 I'll then get redirected to the /auth/:interaction_id
page and eventually redirected to my redirect_uri with access_denied error.然后我将被重定向到/auth/:interaction_id
页面,并最终重定向到我的 redirect_uri 并出现 access_denied 错误。
There's no hint as to why I'm getting access denied.没有任何提示说明为什么我会被拒绝访问。 Upon further digging though, I noticed that when interactionFinished
is called, the interaction key is deleted in Redis.经过进一步挖掘,我注意到当调用interactionFinished
时,Redis 中的交互键被删除。 (Not sure if it's relevant.) (不确定是否相关。)
Any ideas?有任何想法吗?
const provider = new Provider('http://localhost:3333', {
adapter: RedisAdapter,
clients: [
{
client_id: 'foo',
client_secret: 'bar',
redirect_uris: ['http://localhost:3333/launch'],
scope: 'openid',
response_types: ['code'],
},
],
pkce: {
required: () => false,
methods: ['plain'],
},
async findAccount(ctx, id) {
return {
accountId: id,
async claims(use, scope) {
return { sub: id }
},
}
},
claims: {
openid: ['sub'],
email: ['email', 'email_verified'],
phone: ['phone_number', 'phone_number_verified'],
profile: [
'birthdate',
'family_name',
'gender',
'given_name',
'locale',
'middle_name',
'name',
'nickname',
'picture',
'preferred_username',
'profile',
'updated_at',
'website',
'zoneinfo',
],
},
interactions: {
policy: basePolicy,
},
cookies: {
keys: ['some secret key', 'and also the old rotated away some time ago', 'and one more'],
},
jwks: {
keys: [
{
d: 'VEZOsY07JTFzGTqv6cC2Y32vsfChind2I_TTuvV225_-0zrSej3XLRg8iE_u0-3GSgiGi4WImmTwmEgLo4Qp3uEcxCYbt4NMJC7fwT2i3dfRZjtZ4yJwFl0SIj8TgfQ8ptwZbFZUlcHGXZIr4nL8GXyQT0CK8wy4COfmymHrrUoyfZA154ql_OsoiupSUCRcKVvZj2JHL2KILsq_sh_l7g2dqAN8D7jYfJ58MkqlknBMa2-zi5I0-1JUOwztVNml_zGrp27UbEU60RqV3GHjoqwI6m01U7K0a8Q_SQAKYGqgepbAYOA-P4_TLl5KC4-WWBZu_rVfwgSENwWNEhw8oQ',
dp: 'E1Y-SN4bQqX7kP-bNgZ_gEv-pixJ5F_EGocHKfS56jtzRqQdTurrk4jIVpI-ZITA88lWAHxjD-OaoJUh9Jupd_lwD5Si80PyVxOMI2xaGQiF0lbKJfD38Sh8frRpgelZVaK_gm834B6SLfxKdNsP04DsJqGKktODF_fZeaGFPH0',
dq: 'F90JPxevQYOlAgEH0TUt1-3_hyxY6cfPRU2HQBaahyWrtCWpaOzenKZnvGFZdg-BuLVKjCchq3G_70OLE-XDP_ol0UTJmDTT-WyuJQdEMpt_WFF9yJGoeIu8yohfeLatU-67ukjghJ0s9CBzNE_LrGEV6Cup3FXywpSYZAV3iqc',
e: 'AQAB',
kty: 'RSA',
n: 'xwQ72P9z9OYshiQ-ntDYaPnnfwG6u9JAdLMZ5o0dmjlcyrvwQRdoFIKPnO65Q8mh6F_LDSxjxa2Yzo_wdjhbPZLjfUJXgCzm54cClXzT5twzo7lzoAfaJlkTsoZc2HFWqmcri0BuzmTFLZx2Q7wYBm0pXHmQKF0V-C1O6NWfd4mfBhbM-I1tHYSpAMgarSm22WDMDx-WWI7TEzy2QhaBVaENW9BKaKkJklocAZCxk18WhR0fckIGiWiSM5FcU1PY2jfGsTmX505Ub7P5Dz75Ygqrutd5tFrcqyPAtPTFDk8X1InxkkUwpP3nFU5o50DGhwQolGYKPGtQ-ZtmbOfcWQ',
p: '5wC6nY6Ev5FqcLPCqn9fC6R9KUuBej6NaAVOKW7GXiOJAq2WrileGKfMc9kIny20zW3uWkRLm-O-3Yzze1zFpxmqvsvCxZ5ERVZ6leiNXSu3tez71ZZwp0O9gys4knjrI-9w46l_vFuRtjL6XEeFfHEZFaNJpz-lcnb3w0okrbM',
q: '3I1qeEDslZFB8iNfpKAdWtz_Wzm6-jayT_V6aIvhvMj5mnU-Xpj75zLPQSGa9wunMlOoZW9w1wDO1FVuDhwzeOJaTm-Ds0MezeC4U6nVGyyDHb4CUA3ml2tzt4yLrqGYMT7XbADSvuWYADHw79OFjEi4T3s3tJymhaBvy1ulv8M',
qi: 'wSbXte9PcPtr788e713KHQ4waE26CzoXx-JNOgN0iqJMN6C4_XJEX-cSvCZDf4rh7xpXN6SGLVd5ibIyDJi7bbi5EQ5AXjazPbLBjRthcGXsIuZ3AtQyR0CEWNSdM7EyM5TRdyZQ9kftfz9nI03guW3iKKASETqX2vh0Z8XRjyU',
use: 'sig',
},
{
crv: 'P-256',
d: 'K9xfPv773dZR22TVUB80xouzdF7qCg5cWjPjkHyv7Ws',
kty: 'EC',
use: 'sig',
x: 'FWZ9rSkLt6Dx9E3pxLybhdM6xgR5obGsj5_pqmnz5J4',
y: '_n8G69C-A2Xl4xUW2lF0i8ZGZnk_KPYrhv4GbTGu5G4',
},
],
},
features: {
devInteractions: { enabled: true }, // defaults to true
deviceFlow: { enabled: true }, // defaults to false
revocation: { enabled: true }, // defaults to false
},
})
Route.post('/interaction/:uid', async ({ request, response }) => {
const details = await provider.interactionDetails(request.request, response.response)
console.log(details)
console.log(request.input('login'))
return await provider.interactionFinished(request.request, response.response, {
login: {
accountId: request.input('login'),
},
})
})
Okay, I found the solution to my own problem.好的,我找到了解决我自己问题的方法。
What I have missed to mention is that I disabled the consent prompt of the provider.我错过了提及的是我禁用了提供者的同意提示。
The problem is it doesn't generate a "Grant" automatically.问题是它不会自动生成“授予”。 As a result, the middleware "authorization/interaction.js" fails.结果,中间件“authorization/interaction.js”失败了。
if (
!oidc.grant.getOIDCScopeFiltered(oidc.requestParamOIDCScopes)
&& Object.keys(ctx.oidc.resourceServers)
.every(
(resource) => !oidc.grant.getResourceScopeFiltered(resource, oidc.requestParamScopes),
)
) {
throw new errors.AccessDenied(undefined, 'authorization request resolved without requesting interactions but no scope was granted');
}
In order to resolve this, I have to override the loadExistingGrant
and create a new instance of grant if it doesn't exist as explained in the documentation as well.为了解决这个问题,我必须重写loadExistingGrant
并创建一个新的授权实例,如果它不存在,如文档中所述。
You may be required to skip (silently accept) some of the consent checks, while it is discouraged there are valid reasons to do that, for instance in some first-party scenarios or going with pre-existing, previously granted, consents.您可能需要跳过(默默接受)一些同意检查,但不鼓励这样做是有正当理由的,例如在某些第一方场景中或使用预先存在的、先前授予的同意。 To simply silenty "accept" first-party/resource indicated scopes or pre-agreed upon claims use the loadExistingGrant configuration helper function, in there you may just instantiate (and save.) a grant for the current clientId and accountId values.要简单地“接受”第一方/资源指示的范围或预先同意声明,请使用 loadExistingGrant 配置助手 function,您可以在其中实例化(并保存)当前 clientId 和 accountId 值的授权。
async loadExistingGrant(ctx) {
const grantId =
(ctx.oidc.result && ctx.oidc.result.consent && ctx.oidc.result.consent.grantId) ||
ctx.oidc.session.grantIdFor(ctx.oidc.client.clientId)
if (grantId) {
return ctx.oidc.provider.Grant.find(grantId)
}
const grant = new ctx.oidc.provider.Grant({
clientId: ctx.oidc.client.clientId,
accountId: ctx.oidc.session?.accountId,
})
const scopes = ctx.oidc.params.scope.split(' ')
for (let scope of scopes) {
// TODO: Confirm if this is correct
grant.addOIDCScope(scope)
}
// TODO: Use ttl.Grant
await grant.save(3600)
return grant
}
** Disclaimer: I'm still testing the code above but this is the gist of what I did to fix the problem. ** 免责声明:我仍在测试上面的代码,但这是我为解决问题所做的要点。
As a side tip, implement the renderError
config ASAP so that you can see the full stacktrace instead of relying on the UI.作为一个小提示,尽快实现renderError
配置,这样您就可以看到完整的堆栈跟踪,而不是依赖 UI。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.