如何配置node-oidc-provider使id_token包含acr claim?
我在Node应用中集成node-oidc-provider库搭建IDP服务器,配置如下:
{ oidc: { clients: [ { client_id: '', client_secret: '', grant_types: ['implicit', 'authorization_code', 'refresh_token'], redirect_uris: [''], response_types: ['code', 'id_token'], id_token_signed_response_alg: 'RS256', token_endpoint_auth_method: 'client_secret_basic', }, ], findAccount: (ctx, id) => { return Account.findAccount(ctx, id, app); }, conformIdTokenClaims: false, acrValues: ['auth_my_idp'], claims: { openid: ['sub', 'username'], email: ['email', 'email_verified'], profile: ['family_name', 'given_name'] }, clientDefaults: { grant_types: ['authorization_code', 'refresh_token'], id_token_signed_response_alg: 'RS256', response_types: ['code', 'id_token'], token_endpoint_auth_method: 'client_secret_basic', }, interactions: { url(ctx, interaction) { return `/token/${interaction.uid}`; }, proxy: false, forceHTTPS: true, }, pkce: { required: () => false, }, features: { devInteractions: { enabled: false }, claimsParameter: { enabled: true, }, }, adapter: getRedisAdapter(), // setting very short time on sessions ttl: { AccessToken: function AccessTokenTTL() { return 300; }, AuthorizationCode: 300, BackchannelAuthenticationRequest: function BackchannelAuthenticationRequestTTL() { return 300; }, ClientCredentials: function ClientCredentialsTTL() { return 300; }, DeviceCode: 300, Grant: 300, IdToken: 300, Interaction: 300, RefreshToken: function RefreshTokenTTL() { return 300; }, Session: 300, }, }, };
findAccount函数实现如下:
static async findAccount(ctx, id, app) { let response; await app.sql.Person.findOne({ where: { id: id, deleted_at: null }, }).then(person => { response = { accountId: id, async claims(use, scope) { return { sub: person?.email, email: person?.email, }; }, }; }); if (!response) { return undefined; } return response; }
我先发起授权请求获取授权码,请求地址为:
http://localhost:3000/oidc/auth?client_id=oidc_client_id&redirect_uri=http://localhost:8080&state=<state value>&response_type=code&scope=openid%20offline_access%20profile%20email&acr_values=auth_my_idp&nonce=<nonce>&prompt=consent
授权码成功返回至指定重定向地址http://localhost:8080后,我通过Postman调用http://localhost:3000/oidc/token接口获取到id_token,响应如下:
{ "access_token": "an access token", "expires_in": 300, "id_token": "an id token", "scope": "openid offline_access profile email", "token_type": "Bearer" }
但解码id_token后发现其中不存在acr claim。我尝试在findAccount的claims方法中显式返回acr也无效:
async claims(use, scope) { return { sub: person?.email, email: person?.email, acr: 'auth_my_idp' }; },
请问需要添加什么配置,才能让oidc-provider将acr作为claim包含在id_token中?
要让acr claim出现在id_token中,你需要明确:acr是认证上下文引用,属于描述认证方式的元数据,而非用户自身属性,所以不能通过findAccount的claims方法返回,必须在认证流程中主动注入到id_token的声明里。
具体配置步骤:
- 添加认证后钩子注入acr
在oidc配置中新增hooks配置,利用postAuthentication钩子在认证完成后将acr值写入id_token的声明:
{ oidc: { // 保留原有所有配置... hooks: { postAuthentication: async (ctx, session) => { // 从请求参数中获取传入的acr_values,或直接指定固定值 const requestedAcr = ctx.oidc.params.acr_values || 'auth_my_idp'; session.idTokenClaims.acr = requestedAcr; }, }, // 保留原有所有配置... } }
确保认证流程匹配acr值
oidc-provider默认只会在实际使用了与acr_values对应的认证方式时,才会自动添加acr声明。如果你有自定义的认证逻辑(比如对应auth_my_idp的认证步骤),要确保该逻辑被正确触发,这样钩子中的值才符合实际认证上下文。检查
conformIdTokenClaims配置
你当前设置了conformIdTokenClaims: false,这个配置关闭了严格的OIDC规范兼容,不会自动过滤标准声明,所以无需额外调整claims列表,但如果有自定义的声明过滤逻辑,要确保没有排除acr。
另外,如果你是通过交互页面(/token/${interaction.uid})处理认证逻辑,也可以在完成认证时直接将acr设置到session的idTokenClaims中,效果和钩子一致。
内容的提问来源于stack exchange,提问作者Rajiv Neupane

