Azure AD B2C - 令牌验证不起作用
Azure AD B2C - Token validation does not work
我想使用 Azure AD B2C,但在使用时遇到一些困难。
我遇到的一个问题是验证令牌的签名。
首先,我想使用 jwt.io 验证令牌 "manually"。
根据 Microsoft Docs,验证签名应该像这样工作:
Your app can use the kid claim in the JWT header to select the public key in the JSON document that is used to sign a particular token. It can then perform signature validation by using the correct public key and the indicated algorithm.
我的理解:从header中抓取kid值,在jwks_uri位置下的元数据中查找key,(假设)使用"n"的值来验证签名。
但是 Jwt.io、jsonwebtoken.io 和 jose-jwt 都说 签名无效。
我错过了什么?
Jwt.io 似乎只支持带有字符串密码的 HS265 和带有字符串密码或证书的 RS256.
Azure AD B2C 使用更原生的 RS256 形式,根据 RFC 3447, section 3.1 定义 public 密钥由两个部分组成:n
和 e
。 JWK 包含 n
和 e
,可用于生成 public 密钥并验证令牌签名。
为了使用 Jwt.io,您需要将 Azure AD B2C 的 n + e 密钥格式转换为证书格式。有关如何执行此操作的参考,请参阅此示例:
现在有一种方法可以使用两个 npm 包来验证令牌。
资源
信息提供于:Microsoft - Example-verifying-validation-tokens
使用的包版本
如何
下面的示例是根据 Microsoft - Example-verifying-validation-tokens
中的示例修改的
从 Azure 中检索“颁发者”和“用户流”名称值
正在检索应用程序 ID
代码示例
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
包含用户“用户策略”。如果您有多个“用户策略”,则需要弄清楚如何根据每个“用户策略”流程用正确的 jwksUri
实例化 jkwsClient
。
- 虽然您可以利用 jwt.io or jwt.ms,但可以在调试器控制台中解码令牌。这将为您提供一些关键信息,例如“iss”(发行者)和“kid”:
'e3R5cDogIkpXVCIsIGFsZzogIlJTMjU2Iiwga2lkOiAiWU9VX1NORUFLWV9SQUJCSVQifQ==.e2V4cDogMTU5NjYwMTc4NiwgbmJmOiAxNTk2NTk4MTg2LCB2ZXI6ICIxLjAiLCBpc3M6ICJodHRwczovL3doaXRlLXJhYmJpdC5iMmNsb2dpbi5jb20vZjIzNDZhMzBhLTk1ODEtNGU0ZC04MWQwLWQyZjk4NTQ3MWJhOS92Mi4wLyIsIHN1YjogImYzMmNjNmJhLWE5MTctNGE1Ni1hYjhmLWIyNGZmMTg1ODUyOCIsICIuLi4iOiAibW9yZSB2YWx1ZXMuLi4ifQ==.UNODECODEME'.split('.').map((value, index) => {
// The signature can't be decoded
if(index > 1) { return value; }
return atob(value);
});
您可以使用 https://play.golang.org/p/7wWMBOp10R 从模数 (n) 和指数 (e) 中获取 public 密钥。然后将整个输出复制到 jwt.io 中,您将看到签名有效。
我想使用 Azure AD B2C,但在使用时遇到一些困难。 我遇到的一个问题是验证令牌的签名。 首先,我想使用 jwt.io 验证令牌 "manually"。
根据 Microsoft Docs,验证签名应该像这样工作:
Your app can use the kid claim in the JWT header to select the public key in the JSON document that is used to sign a particular token. It can then perform signature validation by using the correct public key and the indicated algorithm.
我的理解:从header中抓取kid值,在jwks_uri位置下的元数据中查找key,(假设)使用"n"的值来验证签名。
但是 Jwt.io、jsonwebtoken.io 和 jose-jwt 都说 签名无效。
我错过了什么?
Jwt.io 似乎只支持带有字符串密码的 HS265 和带有字符串密码或证书的 RS256.
Azure AD B2C 使用更原生的 RS256 形式,根据 RFC 3447, section 3.1 定义 public 密钥由两个部分组成:n
和 e
。 JWK 包含 n
和 e
,可用于生成 public 密钥并验证令牌签名。
为了使用 Jwt.io,您需要将 Azure AD B2C 的 n + e 密钥格式转换为证书格式。有关如何执行此操作的参考,请参阅此示例:
现在有一种方法可以使用两个 npm 包来验证令牌。
资源
信息提供于:Microsoft - Example-verifying-validation-tokens
使用的包版本
如何
下面的示例是根据 Microsoft - Example-verifying-validation-tokens
中的示例修改的从 Azure 中检索“颁发者”和“用户流”名称值
正在检索应用程序 ID
代码示例
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
包含用户“用户策略”。如果您有多个“用户策略”,则需要弄清楚如何根据每个“用户策略”流程用正确的jwksUri
实例化jkwsClient
。- 虽然您可以利用 jwt.io or jwt.ms,但可以在调试器控制台中解码令牌。这将为您提供一些关键信息,例如“iss”(发行者)和“kid”:
'e3R5cDogIkpXVCIsIGFsZzogIlJTMjU2Iiwga2lkOiAiWU9VX1NORUFLWV9SQUJCSVQifQ==.e2V4cDogMTU5NjYwMTc4NiwgbmJmOiAxNTk2NTk4MTg2LCB2ZXI6ICIxLjAiLCBpc3M6ICJodHRwczovL3doaXRlLXJhYmJpdC5iMmNsb2dpbi5jb20vZjIzNDZhMzBhLTk1ODEtNGU0ZC04MWQwLWQyZjk4NTQ3MWJhOS92Mi4wLyIsIHN1YjogImYzMmNjNmJhLWE5MTctNGE1Ni1hYjhmLWIyNGZmMTg1ODUyOCIsICIuLi4iOiAibW9yZSB2YWx1ZXMuLi4ifQ==.UNODECODEME'.split('.').map((value, index) => {
// The signature can't be decoded
if(index > 1) { return value; }
return atob(value);
});
您可以使用 https://play.golang.org/p/7wWMBOp10R 从模数 (n) 和指数 (e) 中获取 public 密钥。然后将整个输出复制到 jwt.io 中,您将看到签名有效。