如何验证Azure AD安全令牌?
如何验证Azure AD安全令牌?
以下代码给我提供了Azure AD安全令牌
,我需要验证该令牌是否有效。如何实现这一点?\n
// 使用客户端凭据获取OAuth令牌 string tenantName = "mytest.onmicrosoft.com"; string authString = "https://login.microsoftonline.com/" + tenantName; AuthenticationContext authenticationContext = new AuthenticationContext(authString, false); // 用于OAuth客户端凭据的配置 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; TaskauthenticationResult = authenticationContext.AcquireTokenAsync(resource, clientCred); token = authenticationResult.Result.AccessToken; Console.WriteLine(token); // 如何在我的服务内验证这个令牌?
如何验证Azure AD安全令牌?
在项目中不使用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; }
这里有一些我在这里使用的函数对你来说是不可用的,它们是自描述的。
我正在使用MS OAuth 2登录到我的网站。我无法在microsoftKeysUrl中找到我的密钥。还有其他地方可以获取正确的密钥吗?
你是说你在令牌中知道密钥的id或名称,但你无法在login.microsoftonline.com/common/discovery/keys中找到它吗?这很奇怪,它应该在那里找到。然而,问题是他们改变了签名算法的版本,并引入了随机数,这会使你很难验证。你使用的是哪个端点来获取令牌,V1还是V2?
经过整整一天的尝试,我终于在这里找到了我的kid。 login.microsoftonline.com/common/discovery/v2.0/keys。但是我必须在AD中启用隐式选项,否则密钥不会显示。
听到这个消息很好。是的,我看到你指定了版本来获取密钥。事情变化很快。
问题的出现原因:
用户使用.net Core 2.0时,需要修改Validate(string token)
方法的两行代码,但是他们并不清楚具体如何修改。
解决方法:
用户需要修改以下两行代码:
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
};
对于问题中提到的具体代码错误,用户可以根据下面提供的链接获取进一步的解决方案。
此外,用户还可以使用OpenSSL来生成自己的SigningKeys。具体的示例可以参考stackoverflow.com/a/10176685/508681上的回答。
对于出现"Microsoft.IdentityModel.Tokens.SecurityTokenException: IDX10612: Decryption failed. Header.Enc is null or empty, it must be specified."错误的情况,目前还没有提供具体的解决方案。
如何验证Azure AD安全令牌?
在验证令牌时有两个步骤。首先,验证令牌的签名以确保该令牌是由Azure Active Directory发布的。其次,基于业务逻辑验证令牌中的声明。
例如,如果您正在开发一个单租户应用程序,则需要验证iss和aud声明。您还需要验证nbf以确保令牌未过期。更多声明请参考此处。
下面的描述是关于签名验证的详细信息。
首先,我们需要检索和缓存签名令牌(公钥)。
public JwtSecurityToken Validate(string token) { string stsDiscoveryEndpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration"; ConfigurationManagerconfigManager = new ConfigurationManager (stsDiscoveryEndpoint); OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result; TokenValidationParameters validationParameters = new TokenValidationParameters { ValidateAudience = false, ValidateIssuer = false, IssuerSigningTokens = config.SigningTokens, ValidateLifetime = false }; JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler(); SecurityToken jwt; var result = tokenHandler.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 IEnumerableGet() { 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支持撤销访问令牌,您可以从此处提交反馈。
目前,无法撤销已由Azure AD发布的访问令牌。但是,我们可以通过在Azure门户上启用“需要用户分配才能访问应用程序”功能并禁用用户来完全禁用用户的登录。如果您想要支持撤销访问令牌,您可以从此处提交反馈。
请不要在示例代码中关闭重要的验证规则(ValidateAudience,ValidateIssuer,ValidateLifetime),以免有人复制粘贴您的示例并认为它已准备好投入生产。
您应该将Validate方法放在哪里并在何处调用它?
我很好奇-谁以及如何管理公钥的缓存,以及在哪些情况下缓存会更新?
但是如何配置config.SigningTokens?Azure AD通过专用URI提供访问密钥。
请参考下面的答案。这个答案已经过时了。SigningTokens不再被Azure使用。
的确,已经更改为SigningKeys,请参考stackoverflow.com/questions/45500752/…