使用node-oidc-provider调用interactionFinished后遇Access Denied问题求助
问题描述
在AdonisJS自定义框架中使用node-oidc-provider时遇到授权失败问题:
- 访问授权链接
http://localhost:3333/auth?client_id=foo&redirect_uri=http://localhost:3333/launch&response_type=code&scope=openid&nonce=123&state=321后,会被重定向至/interaction/:uid页面完成登录提交。 - 调用
interactionFinished后,先跳转至/auth/:interaction_id页面,最终重定向到redirect_uri时出现access_denied错误,无具体原因提示。 - 排查发现调用
interactionFinished后,Redis中的interaction键被删除(不确定是否与问题相关)。
配置
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'), }, }) })
错误截图

解决思路
1. 补充交互完成的授权同意参数
调用interactionFinished时,仅传入login对象不足以完成OIDC授权流程,必须添加consent对象明确同意申请的scope。修改路由代码如下:
Route.post('/interaction/:uid', async ({ request, response }) => { const details = await provider.interactionDetails(request.request, response.response) return await provider.interactionFinished(request.request, response.response, { login: { accountId: request.input('login'), }, consent: { scope: details.params.scope, // 同意请求的所有scope expiresIn: 3600, // 可选:设置授权过期时间 }, }) })
缺少consent是导致access_denied的常见原因,node-oidc-provider会默认拒绝未明确同意的授权请求。
2. 开启详细日志定位问题
在provider配置中添加日志配置,输出debug级别的流程日志,便于查看具体错误原因:
const provider = new Provider('http://localhost:3333', { // 其他配置保持不变 logger: { level: 'debug', stream: process.stdout, }, })
运行后查看控制台输出,日志会详细记录授权流程中的每一步,包括参数验证、决策判断等环节的错误信息。
3. 验证Redis适配器的正确性
Redis中的interaction键被删除是正常行为(交互完成后会清理数据),但需确认适配器实现是否符合规范:
- 检查适配器的
upsert、find、destroy方法是否正确处理数据。 - 临时切换为内存适配器(注释掉
adapter: RedisAdapter)测试,如果问题消失,说明Redis适配器存在bug。
4. 检查自定义交互策略
当前配置使用了basePolicy,需确认该策略是否存在限制授权的逻辑。可以临时注释掉策略配置,使用默认策略测试:
interactions: { // policy: basePolicy, // 注释此行 },
如果问题解决,说明自定义策略中存在拒绝授权的规则,需调整策略逻辑。
5. 校验请求参数一致性
确保从初始授权请求到交互完成的所有参数完全一致:
- 确认
redirect_uri与客户端配置中的值完全匹配(包括协议、域名、端口、路径)。 - 检查
nonce、state参数在流程中未被篡改或丢失。
内容的提问来源于stack exchange,提问作者Renzo Sunico
相关产品推荐
相关产品推荐

