我想使用Azure AD B2C,但使用它有几个困难。 我遇到的一个问题是验证令牌的签名。 首先,我想手动验证令牌"#34;使用jwt.io.
根据Microsoft Docs,验证签名应该是这样的:
您的应用可以使用JWT标头中的小孩声明来选择JSON文档中用于签署特定标记的公钥。然后,它可以使用正确的公钥和指示的算法执行签名验证。
我的理解:从标题中抓取kid值,在jwks_uri的位置下查找元数据中的键,(假设)使用" n"的值。验证签名。
但Jwt.io,jsonwebtoken.io和jose-jwt都说, siganture无效。
我错过了什么?
答案 0 :(得分:4)
Jwt.io似乎只支持带字符串密码的HS265和带字符串密码的RS256或证书。
Azure AD B2C使用更原生的RS256格式,根据RFC 3447, section 3.1定义公钥包含两个组件:n
和e
。 JWK包含n
和e
,可用于生成公钥并验证令牌签名。
为了使用Jwt.io,您需要将Azure AD B2C的密钥的n + e格式转换为证书格式。请参阅此示例,以获取有关如何执行此操作的参考:Go Language Convert Modulus exponent to X.509 certificate
答案 1 :(得分:0)
现在有一种使用两个npm软件包验证令牌的方法。
该信息可从以下网址获得:Microsoft - Example-verifying-validation-tokens
下面的示例是对Microsoft - Example-verifying-validation-tokens中的示例所做的修改
import jwt from 'jsonwebtoken';
import jkwsClient from 'jwks-rsa';
// Variables that need to be defined based on your Azure B2C Configuration.
const jwksUri = 'https://MY-B2C-TENANT.b2clogin.com/MY-B2C-TENANT.onmicrosoft.com/MY-USER-FLOW-NAME/discovery/v2.0/keys';
const client = jkwsClient({
jwksUri
});
export function getKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
var signingKey = key.getPublicKey();
callback(null, signingKey);
});
}
export function isTokenValid(token, applicationId, issuerUri) {
return new Promise((resolve) => {
const options = {
audience: [applicationId],
issuer: [issuerUri]
};
jwt.verify(token, getKey, options, (err, decoded) => {
if (err) {
// eslint-disable-next-line no-console
console.error('Jwt Validation Failed', err);
resolve(false);
} else {
// eslint-disable-next-line no-console
console.debug(decoded)
resolve(true);
}
});
});
}
const applicationId = 'APPLICATION ID OF B2C TENANT'
const issuerUri = 'In the User Flow Properties'
const valid = isTokenValid('some token value without "Bearer", applicationId, issuerUri);
jwksUri
包含用户“用户策略”。如果您有多个“用户策略”,则需要根据每个“用户策略”流来弄清楚如何用正确的jkwsClient
实例化jwksUri
。'e3R5cDogIkpXVCIsIGFsZzogIlJTMjU2Iiwga2lkOiAiWU9VX1NORUFLWV9SQUJCSVQifQ==.e2V4cDogMTU5NjYwMTc4NiwgbmJmOiAxNTk2NTk4MTg2LCB2ZXI6ICIxLjAiLCBpc3M6ICJodHRwczovL3doaXRlLXJhYmJpdC5iMmNsb2dpbi5jb20vZjIzNDZhMzBhLTk1ODEtNGU0ZC04MWQwLWQyZjk4NTQ3MWJhOS92Mi4wLyIsIHN1YjogImYzMmNjNmJhLWE5MTctNGE1Ni1hYjhmLWIyNGZmMTg1ODUyOCIsICIuLi4iOiAibW9yZSB2YWx1ZXMuLi4ifQ==.UNODECODEME'.split('.').map((value, index) => {
// The signature can't be decoded
if(index > 1) { return value; }
return atob(value);
});