You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

node-oidc-provider 7.12.0:UserInfo端点JWT令牌invalid_token错误

Node-oidc-provider 7.12.0 启用resourceIndicators后JWT类型AccessToken调用UserInfo端点报invalid_token错误

在使用node-oidc-provider 7.12.0的authorization_code模式时,启用resourceIndicators配置后,使用JWT类型AccessToken调用UserInfo(/me)端点会返回invalid_token错误;注释掉resourceIndicators部分则功能正常。可通过PostMan正常获取AccessToken,但携带该令牌调用/me端点时失败。已尝试多种配置均未解决,相关代码及生成的JWT内容如下:

const {Provider} = require('oidc-provider');

let hostname = process.env.HOSTNAME;
if (hostname === undefined) {
    hostname = "http://localhost"
}
const port = process.env.PORT || 3000;

if (port !== 80 && port !== 443) {
    hostname = hostname + ':' + port
}

const users = [
    {
        "id": "user1",
        "email": "user1@example.com",
        "authentication_method_reference": "mfa"
    }
]

const clients = [
    {
        "client_id": "client-1",
        "client_secret": "client-1-secret",
        "redirect_uris": [
            "http://localhost:3000"
        ]
    }
]

async function findAccount (ctx, id) {
    // This would ideally be just a check whether the account is still in your storage
    let account = users.find(user => {
        return user.id === id;
    })

    if (!account) {
        return undefined;
    }

    return {
        accountId: id,
        async claims() {
            return {
                sub: id,
                email: account.email,
                amr: [account.authentication_method_reference]
            };
        },
    };
}

const configuration = {
    clients: clients,
    conformIdTokenClaims: false,
    features: {
        devInteractions: {
            enabled: true
        },
        resourceIndicators: {
            defaultResource: (ctx, client, oneOf) => {
                return hostname;
            },
            enabled: true,
            getResourceServerInfo: (ctx, resourceIndicator, client) => {
                console.log('get resource server info', client);
                return ({
                    audience: resourceIndicator,
                    scope: 'openid',
                    accessTokenTTL: 2 * 60 * 60,
                    accessTokenFormat: 'jwt',
                });
            },
            useGrantedResource: (ctx, model) => { return true; }
        }
    },
    claims: {
        openid: [
            'sub',
            'email',
            'amr'
        ]
    },
    cookies: {
        keys: 'super,secret'.split(',')
    },
    pkce: {
        required: () => false
    },
    // Used to skip the 'approval' page
    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) {
            // keep grant expiry aligned with session expiry
            // to prevent consent prompt being requested when grant expires
            const grant = await ctx.oidc.provider.Grant.find(grantId);

            // this aligns the Grant ttl with that of the current session
            // if the same Grant is used for multiple sessions, or is set
            // to never expire, you probably do not want this in your code
            if (ctx.oidc.account && grant.exp < ctx.oidc.session.exp) {
                grant.exp = ctx.oidc.session.exp;

                await grant.save();
            }

            return grant;
        } else {
            const grant = new ctx.oidc.provider.Grant({
                clientId: ctx.oidc.client.clientId,
                accountId: ctx.oidc.session.accountId,
            });

            grant.addOIDCScope('openid');
            grant.addResourceScope(hostname, 'openid');

            await grant.save();

            return grant;
        }
    },
    extraTokenClaims: async (ctx, token) => {
        return findAccount(ctx, token.accountId).then(account => {
            return account.claims()
        })
    },
    findAccount: findAccount
};

const oidc = new Provider(hostname, configuration);

function handleServerError(ctx, err) {
    console.log(err);
}

function handleGrantErrors({headers: {authorization}, oidc: {body, client}}, err) {
    console.log(err);
}

function handleAccessToken(token) {
    console.log(token);
}

oidc.on('grant.error', handleGrantErrors);
oidc.on('introspection.error', handleGrantErrors);
oidc.on('revocation.error', handleGrantErrors);
oidc.on('server_error', handleServerError);
oidc.on('access_token.issued', handleAccessToken);

oidc.listen(port, () => {
    console.log(`oidc-provider listening on port ${port}.`)
})

生成的JWT内容:

{
  "sub": "user1",
  "email": "user1@example.com",
  "amr": [
    "mfa"
  ],
  "jti": "-7gURc8Y1SXqOXhWR691i",
  "iat": 1668777371,
  "exp": 1668784571,
  "scope": "openid",
  "client_id": "client-1",
  "iss": "http://localhost:3000",
  "aud": "http://localhost:3000"
}

内容的提问来源于stack exchange,提问作者F0x06

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 02:35:32