web-dev-qa-db-fra.com

Comment valider le jeton de sécurité Azure AD?

Le code suivant me donne Azure AD security token, je dois valider que le jeton est valide ou non. Comment y parvenir?

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

Il y a deux étapes pour vérifier le jeton. Commencez par vérifier la signature du jeton pour vous assurer qu'il a été émis par Azure Active Directory. Deuxièmement, vérifiez les revendications dans le jeton en fonction de la logique métier. 

Par exemple, nous devons vérifier les revendications iss et aud si vous développiez une application à locataire unique. Et vous devez également vérifier la nbf pour vous assurer que le jeton n’est pas expiré. Plus de demandes que vous pouvez consulter ici

La description ci-dessous est tirée de ici sur le détail de la vérification de la signature. (Remarque: l'exemple ci-dessous utilise le point de terminaison Azure AD v2. Vous devez utiliser le point de terminaison qui correspond au point de terminaison utilisé par l'application cliente.)

Le jeton d'accès à partir d'Azure AD est un jeton Web JSON (JWT) signé par le service Token de sécurité dans une clé privée.

Le JWT comprend 3 parties: en-tête, données et signature. Techniquement, nous pouvons utiliser la clé publique pour valider le jeton d'accès.

Première étape - récupérer et mettre en cache les jetons de chant (clé publique)

Point de terminaison: https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration

Ensuite, nous pouvons utiliser la variable JwtSecurityTokenHandler pour vérifier le jeton à l'aide de l'exemple de code ci-dessous:

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

Et si vous utilisiez les composants OWIN dans votre projet, il est plus facile de vérifier le jeton. Nous pouvons utiliser le code ci-dessous pour vérifier le jeton:

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

Ensuite, nous pouvons utiliser le code ci-dessous pour vérifier la "portée" dans le jeton:

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

Et voici un exemple de code qui protégeait l'API Web avec Azure AD:

Protéger une API Web à l'aide de jetons de support Azure AD

18
Fei Xue

Je voulais juste ajouter à la réponse de Fei pour les personnes utilisant .net Core 2.0

Vous devrez modifier 2 lignes de la méthode Validate(string token).

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

...
 TokenValidationParameters validationParameters = new TokenValidationParameters
 {
     ValidateAudience = true,
     ValidateIssuer = true,
     IssuerSigningKeys = config.SigningKeys, //.net core calls it "IssuerSigningKeys" and "SigningKeys"
     ValidateLifetime = true
 };
4
Ambrose Leung

Mais si vous n'utilisez pas OWIN dans vos projets, cela va être un peu difficile ou au moins beaucoup de temps .. This article Here est une excellente ressource.

Et parce que je n’ai pas grand chose à ajouter à ce qui précède, à l’exception du code détaillé .. Voici quelque chose qui peut vous être utile:

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

Certaines fonctions que j’utilise ici ne sont pas disponibles pour vous, elles sont auto-descriptives.

1
Assil