次のコードは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?
トークンを検証するには、2つの手順があります。まず、トークンの署名を確認して、Azure Active Directoryによってトークンが発行されたことを確認します。次に、ビジネスロジックに基づいてトークン内のクレームを確認します。
たとえば、単一のテナントアプリを開発している場合は、iss
およびaud
クレームを検証する必要があります。また、nbf
を検証して、トークンの有効期限が切れていないことを確認する必要もあります。参照できるその他のクレーム こちら 。
以下の説明は、署名検証の詳細について here からのものです。 (注:以下の例では、Azure AD v2エンドポイントを使用しています。クライアントアプリが使用しているエンドポイントに対応するエンドポイントを使用する必要があります。)
Azure ADからのアクセストークンは、プライベートキーでセキュリティトークンサービスによって署名されたJSON Web Token(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"
});
}
...
}
そして、Azure ADでWeb APIを保護したコードサンプルを次に示します。
.net Core 2.0を使用している人々に対するFeiの回答に追加したかっただけです
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
};
ただし、プロジェクトでOWINを使用していない場合は、少し難しいか、少なくとも時間がかかります。この記事 Here は素晴らしいリソースです。
そして、詳細なコードを除いて、上記に追加することはあまりないためです。
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;
}
ここで使用している機能のうち、利用できないものがありますが、それらは自己記述的です。