简体   繁体   English

如何验证Azure AD安全令牌?

[英]How to validate Azure AD security token?

The following code gives me Azure AD security token , I need to validate that token is valid or not. 以下代码为我提供了Azure AD security token ,我需要验证该令牌是否有效。 How to achieve this? 怎么做到这一点?

// Get OAuth token using client credentials 
string tenantName = "mytest.onmicrosoft.com";
string authString = "https://login.microsoftonline.com/" + tenantName;

AuthenticationContext authenticationContext = new AuthenticationContext(authString, false);

// Config for OAuth client credentials  
string clientId = "fffff33-6666-4888-a4tt-fbttt44444";
string key = "123v47o=";
ClientCredential clientCred = new ClientCredential(clientId, key);
string resource = "http://mytest.westus.cloudapp.azure.com";
string token;

Task<AuthenticationResult> authenticationResult = authenticationContext.AcquireTokenAsync(resource, clientCred);
token = authenticationResult.Result.AccessToken;
Console.WriteLine(token);
// How can I validate this token inside my service?                

There are two steps to verify the token. 验证令牌有两个步骤。 First, verify the signature of the token to ensure the token was issued by Azure Active Directory. 首先,验证令牌的签名以确保令牌是由Azure Active Directory颁发的。 Second, verify the claims in the token based on the business logic. 其次,根据业务逻辑验证令牌中的声明。

For example, we need to verify the iss and aud claim if you were developing a single tenant app. 例如,如果您正在开发单个租户应用程序,我们需要验证issaud声明。 And you also need to verify the nbf to ensure the token is not expired. 您还需要验证nbf以确保令牌未过期。 More claims you can refer here . 您可以在此处参考更多声明。

Below description is from here about the detail of signature verifying. 下面的说明是从这里大约签名验证的细节。 (Note: The example below uses the Azure AD v2 endpoint. You should use the endpoint that corresponds to the endpoint the client app is using.) (注意:下面的示例使用Azure AD v2端点。您应该使用与客户端应用程序正在使用的端点对应的端点。)

The access token from the Azure AD is a JSON Web Token(JWT) which is signed by Security Token Service in private key. Azure AD中的访问令牌是JSON Web令牌(JWT),由安全令牌服务在私钥中签名。

The JWT includes 3 parts: header, data, and signature. JWT包括3个部分:标题,数据和签名。 Technically, we can use the public key to validate the access token. 从技术上讲,我们可以使用公钥来验证访问令牌。

First step – retrieve and cache the singing tokens (public key) 第一步 - 检索并缓存歌唱令牌(公钥)

Endpoint: https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration 端点: https//login.microsoftonline.com/common/v2.0/.well-known/openid-configuration

Then we can use the JwtSecurityTokenHandler to verify the token using the sample code below: 然后我们可以使用JwtSecurityTokenHandler使用下面的示例代码验证令牌:

 public JwtSecurityToken Validate(string token)
 {
     string stsDiscoveryEndpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";

     ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint);

     OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;

     TokenValidationParameters validationParameters = new TokenValidationParameters
     {
         ValidateAudience = false,
         ValidateIssuer = false,
         IssuerSigningTokens = config.SigningTokens,
         ValidateLifetime = false
     };

     JwtSecurityTokenHandler tokendHandler = new JwtSecurityTokenHandler();

     SecurityToken jwt;

     var result = tokendHandler.ValidateToken(token, validationParameters, out jwt);

     return jwt as JwtSecurityToken;
 }

And if you were using the OWIN components in your project, it is more easy to verify the token. 如果您在项目中使用OWIN组件,则更容易验证令牌。 We can use the code below to verify the token: 我们可以使用以下代码验证令牌:

app.UseWindowsAzureActiveDirectoryBearerAuthentication(
            new WindowsAzureActiveDirectoryBearerAuthenticationOptions
            {
                Audience = ConfigurationManager.AppSettings["ida:Audience"],
                Tenant = ConfigurationManager.AppSettings["ida:Tenant"]
            });

Then we can use the code below to verify the 'scope' in the token: 然后我们可以使用下面的代码来验证令牌中的“范围”:

public IEnumerable<TodoItem> Get()
{
    // user_impersonation is the default permission exposed by applications in AAD
    if (ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/scope").Value != "user_impersonation")
    {
        throw new HttpResponseException(new HttpResponseMessage {
          StatusCode = HttpStatusCode.Unauthorized,
          ReasonPhrase = "The Scope claim does not contain 'user_impersonation' or scope claim not found"
        });
    }
    ...
}

And here is a code sample which protected the web API with Azure AD: 这是一个使用Azure AD保护Web API的代码示例:

Protect a Web API using Bearer tokens from Azure AD 使用Azure AD中的承载令牌保护Web API

Just wanted to add to Fei's answer for people using .net Core 2.0 只是想为使用.net Core 2.0的人添加Fei的答案

You'll have to modify 2 lines of the Validate(string token) method. 您将不得不修改Validate(string token)方法的2行。

 var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
        stsDiscoveryEndpoint,
        new OpenIdConnectConfigurationRetriever()); //1. need the 'new OpenIdConnect...'

 OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;
 TokenValidationParameters validationParameters = new TokenValidationParameters
 {
     //decode the JWT to see what these values should be
     ValidAudience = "some audience",
     ValidIssuer = "some issuer",

     ValidateAudience = true,
     ValidateIssuer = true,
     IssuerSigningKeys = config.SigningKeys, //2. .NET Core equivalent is "IssuerSigningKeys" and "SigningKeys"
     ValidateLifetime = true
 };

But if you are not using OWIN in your projects, it is going to be a little hard or at least time consuming.. This article Here is great resource. 但是如果你没有在你的项目中使用OWIN,那将会有点困难或者至少耗费时间。本文是一个很好的资源。

And because I do not have much to add on the above, except the detailed code.. Here is something that can be useful to you: 而且因为除了详细的代码之外,我没有太多要补充的内容。这里有一些对你有用的东西:

 public async Task<ClaimsPrincipal> CreatePrincipleAsync()
    {
        AzureActiveDirectoryToken azureToken = Token.FromJsonString<AzureActiveDirectoryToken>();
        var allParts = azureToken.IdToken.Split(".");
        var header = allParts[0];
        var payload = allParts[1];
        var idToken = payload.ToBytesFromBase64URLString().ToAscii().FromJsonString<AzureActiveDirectoryIdToken>();

        allParts = azureToken.AccessToken.Split(".");
        header = allParts[0];
        payload = allParts[1];
        var signature = allParts[2];
        var accessToken = payload.ToBytesFromBase64URLString().ToAscii().FromJsonString<AzureActiveDirectoryAccessToken>();

        var accessTokenHeader = header.ToBytesFromBase64URLString().ToAscii().FromJsonString<AzureTokenHeader>();
        var isValid = await ValidateToken(accessTokenHeader.kid, header, payload, signature);
        if (!isValid)
        {
            throw new SecurityException("Token can not be validated");
        }
        var principal = await CreatePrincipalAsync(accessToken, idToken);
        return principal;
    }



    private async Task<bool> ValidateToken(string kid, string header, string payload, string signature)
    {
        string keysAsString = null;
        const string microsoftKeysUrl = "https://login.microsoftonline.com/common/discovery/keys";

        using (var client = new HttpClient())
        {
            keysAsString = await client.GetStringAsync(microsoftKeysUrl);
        }
        var azureKeys = keysAsString.FromJsonString<MicrosoftConfigurationKeys>();
        var signatureKeyIdentifier = azureKeys.Keys.FirstOrDefault(key => key.kid.Equals(kid));
        if (signatureKeyIdentifier.IsNotNull())
        {
            var signatureKey = signatureKeyIdentifier.x5c.First();
            var certificate = new X509Certificate2(signatureKey.ToBytesFromBase64URLString());
            var rsa = certificate.GetRSAPublicKey();
            var data = string.Format("{0}.{1}", header, payload).ToBytes();

            var isValidSignature = rsa.VerifyData(data, signature.ToBytesFromBase64URLString(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
            return isValidSignature;
        }

        return false;
    }

There are some functions that I use in here that are not available for you, they are self descriptive. 我在这里使用的一些功能是不可用的,它们是自描述的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM