azure 验证颁发者使用颁发者2或颁发者1?

wbrvyc0a  于 2023-10-22  发布在  其他
关注(0)|答案(2)|浏览(114)

我尝试遵循https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-webapi-manual-jwt-validation/中的示例验证代码,其中声明使用Issuer 1。
(实际上代码在https://github.com/Azure-Samples/active-directory-dotnet-webapi-manual-jwt-validation/blob/master/TodoListService-ManualJwt/Global.asax.cs#L136)

  • 我从以下位置获取颁发者/签名密钥:https://login.microsoftonline.com/efa3038a-575b-42ea-8ba1-483cf7f0bdb6/.well-known/openid-configuration
  • 获取颁发者:"issuer":"https://sts.windows.net/efa3038a-575b-42ea-8ba1-483cf7f0bdb6/"
  • 我取回了我的AUTH代币:https://login.microsoftonline.com/efa3038a-575b-42ea-8ba1-483cf7f0bdb6/oauth2/v2.0/authorize?client_id=463f8472-dff3-40d0-8ec5-da2d9ba9c348&response_type=id_token&redirect_uri=http%3A%2F%2Flocalhost:30662&scope=openid%20profile&response_mode=fragment&state=12345&nonce=678910
  • 它会产生一个有效的token:eyJ0eX...De0GVw
  • 其将(via var jwt = new Microsoft.IdentityModel.JsonWebTokens.JsonWebToken(authorizationHeader.Substring(kBearer_.Length)))解码为:....

Issuer: "https://login.microsoftonline.com/efa3038a-575b-42ea-8ba1-483cf7f0bdb6/v2.0"
然后,我尝试使用以下方法进行验证:

TokenValidationParameters validationParams = new TokenValidationParameters
{
    // We accept both the App Id URI and the AppId of this service application
    ValidAudiences = new[] { kADConfiguration_.Audience, kADConfiguration_.ClientId },

    // Supports both the Azure AD V1 and V2 endpoint
    ValidIssuers = new[] { _issuer, $"{_issuer}/v2.0" },
    ValidateIssuer = true,  // set to false and works, set to true it fails

    IssuerSigningKeys = validationInfo.Item2
};
Microsoft.IdentityModel.Tokens.SecurityToken v;
System.Security.Claims.ClaimsPrincipal answer = handler.ValidateToken(authorizationHeader.Substring(kBearer_.Length), validationParams, out v);

我可以看到令牌中的颁发者与https://login.microsoftonline.com/efa3038a-575b-42ea-8ba1-483cf7f0bdb6/.well-known/openid-configuration中的颁发者不同(只是主机名部分
但我不知道为什么,或者我做错了什么。
我还没有找到任何有用的文档来说明要传递给验证过程的参数(例如,https://learn.microsoft.com/en-us/dotnet/api/system.identitymodel.tokens.jwt.jwtsecuritytokenhandler.validatetoken?view=azure-dotnet只说“validationParameters Contains validation parameters”,而https://learn.microsoft.com/en-us/dotnet/api/microsoft.identitymodel.tokens.tokenvalidationparameters.validissuers?view=azure-dotnet#Microsoft_IdentityModel_Tokens_TokenValidationParameters_ValidIssuers说“contains valid issuers that will be used to check against the token's issuer”。

ewm0tg9j

ewm0tg9j1#

您当前看到的示例有点旧,并使用Azure AD v1.0端点作为参考进行解释。您在令牌中看到的颁发者值是正确的,因为您已从Azure AD v2.0终结点获取了该令牌。您用于查找有效颁发者的OpenID发现文档URL不正确。更多的解释在进一步的章节。
我还应该简要地提到,在大多数情况下,像sample you're following解释的那样显式地手动验证令牌是一件不必要的繁重工作。我不想偏离你的原始问题,因此我只是在我的回答的最后保留了一些关于这一部分的指针,但是看看它是否对你的情况有意义。

有关从Azure AD v1.0和v2.0端点获取的访问令牌的更多详细信息

请查看此Microsoft文档-访问令牌参考-示例令牌

  • 示例v2.0令牌-在jwt.ms上解码-

请注意,这一个具有颁发者格式

https://login.microsoftonline.com/<Azure AD Tenant GUID>/v2.0
  • 示例v1.0令牌-在jwt.ms上解码-

请注意,这一个具有颁发者格式

https://sts.windows.net/<Azure AD Tenant GUID>/

租户的OpenID发现文档URL

对于openid配置,您应该查看Azure AD v2.0端点,您将在那里看到正确的颁发者值。特别是对于您的租户(如共享问题)正确的URL使用将是

https://login.microsoftonline.com/efa3038a-575b-42ea-8ba1-483cf7f0bdb6/v2.0/.well-known/openid-configuration

您当前查看的OpenID Discovery文档的值仅适用于从v1.0端点获取的令牌。

如何从Azure门户查找正确的OpenID发现文档URL

对于v2.0端点,请进行预览体验,如下所示。Azure门户> Azure Active Directory >应用程序注册(预览版)>端点

对于v1.0端点,转到旧体验(即将消失)。Azure门户> Azure Active Directory >应用程序注册>端点

正如我最初所说的,对于大多数应用程序来说,手动令牌验证通常是不需要的。
对于单租户应用,一般只需要保留`` ValidateIssuer = true for the TokenValidationParameters`
在多租户应用程序的情况下,可能有几种情况。

  • 如果你事先知道你的发行者,你仍然可以设置ValidateIssuer=True和设置ValidIssuers的列表。ValidIssuers = new List<string>()...
  • 如果你的应用程序的有效发行者是动态的,或者如果你想写一些逻辑来收集这个列表,你可以为TokenValidationParameters.IssuerValidator写一个实现,它有你的自定义逻辑。您只需要设置一个将用于验证颁发者的委托。
  • 在其他情况下,您仍然希望编写自己的自定义逻辑,那么像您所遵循的示例这样的显式验证是有意义的。

请在这里查看相关的帖子。

50pmv0ei

50pmv0ei2#

虽然上面标记的答案很有帮助,但它并不完全是一个答案。如果有人遇到这个问题,这里有一段代码可以用来解决上述问题:
...

private static AppSettings.ADConfiguration kADConfiguration_ = AppSettings.PeekObj<AppSettings.AuthorizationConfiguration>(AppSettings.kFieldname_Authorization).ADConfiguration;

private static string kAuthority_ = String.Format(CultureInfo.InvariantCulture, kADConfiguration_.AADInstance, kADConfiguration_.Tenant);

private static string _issuer = string.Empty;
private static ICollection<SecurityKey> _signingKeys = null;
private static DateTime _stsMetadataRetrievalTime = DateTime.MinValue;

private static Tuple<string, ICollection<SecurityKey>> AssureSigningKeysAndIssuerUpToDate_()
{
    /*
     *  This logic is based on https://github.com/Azure-Samples/active-directory-dotnet-webapi-manual-jwt-validation/blob/master/TodoListService-ManualJwt/Global.asax.cs#L136 ,
     *     as amended in https://stackoverflow.com/questions/55840510/validating-the-issuer-token-has-issuer-https-login-microsoftonline-com-xv2-0
     */

    // The issuer and signingKeys are cached for 24 hours. They are updated if any of the conditions in the if condition is true.
    if (DateTime.UtcNow.Subtract(_stsMetadataRetrievalTime).TotalHours > 24
    || string.IsNullOrEmpty(_issuer)
    || _signingKeys == null)
    {
        // Get tenant information that's used to validate incoming jwt tokens
        string stsDiscoveryEndpoint = $"{kAuthority_}/v2.0/.well-known/openid-configuration";
        var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
        OpenIdConnectConfiguration config = null;
        Task.Run(async () => config = await configManager.GetConfigurationAsync()).Wait();
        _issuer = config.Issuer;
        _signingKeys = config.SigningKeys;
        _stsMetadataRetrievalTime = DateTime.UtcNow;
    }
    return new Tuple<string, ICollection<SecurityKey>>(_issuer, _signingKeys);
}

...

public String GetUserFromAuthHeader(String authorizationHeader)
{
    const String kBearer_ = "Bearer ";
    if (authorizationHeader.StartsWith(kBearer_))
    {
        System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler handler = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler();

        Tuple<string, ICollection<SecurityKey>> issuerAndSigningKeys = AssureSigningKeysAndIssuerUpToDate_();
        TokenValidationParameters validationParams = new TokenValidationParameters
        {
            // We accept both the App Id URI and the AppId of this service application
            ValidAudiences = new[] { kADConfiguration_.Audience, kADConfiguration_.ClientId },
            ValidIssuers = new[] { issuerAndSigningKeys.Item1, $"{issuerAndSigningKeys.Item1}/v2.0" },
            IssuerSigningKeys = issuerAndSigningKeys.Item2
        };

        SecurityToken v;
        System.Security.Claims.ClaimsPrincipal answer = handler.ValidateToken(authorizationHeader.Substring(kBearer_.Length), validationParams, out v);

        //DEBUG - var jwt = new Microsoft.IdentityModel.JsonWebTokens.JsonWebToken(authorizationHeader.Substring(kBearer_.Length));
        var claims = answer.Claims;
        foreach (System.Security.Claims.Claim c in claims)
        {
            if (c.Type == "preferred_username")
            {
                return c.Value;
            }
        }
    }
    return null;
}

相关问题