Как проверить токен безопасности Azure AD?

Следующий код дает мне Azure AD security token, мне нужно проверить, является ли этот токен действительным или нет. Как этого добиться?

// 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?                

Ответ 1

Есть два шага для проверки токена. Сначала проверьте подпись токена, чтобы убедиться, что токен был выдан Azure Active Directory. Во-вторых, проверьте утверждения в токене на основе бизнес-логики.

Например, нам нужно проверить утверждение iss и aud если вы разрабатывали приложение для одного арендатора. И вам также необходимо проверить nbf чтобы убедиться, что токен не истек. Больше претензий вы можете сослаться здесь.

Ниже описание от здесь о деталях подписи подтверждать. (Примечание. В приведенном ниже примере используется конечная точка Azure AD v2. Следует использовать конечную точку, соответствующую конечной точке, используемой клиентским приложением.)

Маркер доступа из Azure AD представляет собой веб-токен JSON (JWT), подписанный службой токенов безопасности в виде закрытого ключа.

JWT включает в себя 3 части: заголовок, данные и подпись. Технически, мы можем использовать открытый ключ для проверки токена доступа.

Первый шаг - извлечение и кеширование поющих токенов (открытый ключ)

Конечная точка: https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration

Затем мы можем использовать 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;
 }

И если вы использовали компоненты OWIN в своем проекте, то проще проверить токен. Мы можем использовать код ниже для проверки токена:

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

Затем мы можем использовать приведенный ниже код, чтобы проверить область действия в токене:

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"
        });
    }
    ...
}

А вот пример кода, который защищает веб-API с помощью Azure AD:

Защитите веб-API с помощью токенов на предъявителя из Azure AD

Ответ 2

Просто хотел добавить в Fei ответ для людей, использующих .net Core 2.0

Вам нужно изменить 2 строки метода Validate(string token).

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

...
 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, //.net core calls it "IssuerSigningKeys" and "SigningKeys"
     ValidateLifetime = true
 };

Ответ 3

Но если вы не используете Owin в своих проектах, он будет немного трудно или, по крайней мере, много времени.. Эта статья здесь является большим ресурсом.

И потому, что мне нечего добавить к вышесказанному, кроме подробного кода.. Вот кое-что, что может быть полезно для вас:

 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;
    }

Здесь я использую некоторые функции, которые вам недоступны, они описательны.

Ответ 4

Доброе утро ! Я прочитал ответы, но вы можете сказать мне библиотеки, которые мне нужны для использования ConfigurationManager configManager = new ConfigurationManager (stsDiscoveryEndpoint);

 OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;

в первом ответе?

Спасибо !!!