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?
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:
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
};
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.