private async Task SkillValidation_ValidateIdentityAsync(ClaimsIdentity identity, CancellationToken cancellationToken) { if (identity == null) { // No valid identity. Not Authorized. throw new UnauthorizedAccessException("Invalid Identity"); } if (!identity.IsAuthenticated) { // The token is in some way invalid. Not Authorized. throw new UnauthorizedAccessException("Token Not Authenticated"); } var versionClaim = identity.Claims.FirstOrDefault(c => c.Type == AuthenticationConstants.VersionClaim); if (versionClaim == null) { // No version claim throw new UnauthorizedAccessException($"'{AuthenticationConstants.VersionClaim}' claim is required on skill Tokens."); } // Look for the "aud" claim, but only if issued from the Bot Framework var audienceClaim = identity.Claims.FirstOrDefault(c => c.Type == AuthenticationConstants.AudienceClaim)?.Value; if (string.IsNullOrWhiteSpace(audienceClaim)) { // Claim is not present or doesn't have a value. Not Authorized. throw new UnauthorizedAccessException($"'{AuthenticationConstants.AudienceClaim}' claim is required on skill Tokens."); } if (!await _credentialFactory.IsValidAppIdAsync(audienceClaim, cancellationToken).ConfigureAwait(false)) { // The AppId is not valid. Not Authorized. throw new UnauthorizedAccessException("Invalid audience."); } var appId = JwtTokenValidation.GetAppIdFromClaims(identity.Claims); if (string.IsNullOrWhiteSpace(appId)) { // Invalid appId throw new UnauthorizedAccessException("Invalid appId."); } }
// The following code is based on EmulatorValidation.AuthenticateEmulatorToken private async Task <ClaimsIdentity> EmulatorValidation_AuthenticateEmulatorTokenAsync(string authHeader, ServiceClientCredentialsFactory credentialFactory, HttpClient httpClient, string channelId, AuthenticationConfiguration authConfiguration, CancellationToken cancellationToken) { var toBotFromEmulatorTokenValidationParameters = new TokenValidationParameters() { ValidateIssuer = true, ValidIssuers = new[] { // TODO: presumably this table should also come from configuration "https://sts.windows.net/d6d49420-f39b-4df7-a1dc-d59a935871db/", // Auth v3.1, 1.0 token "https://login.microsoftonline.com/d6d49420-f39b-4df7-a1dc-d59a935871db/v2.0", // Auth v3.1, 2.0 token "https://sts.windows.net/f8cdef31-a31e-4b4a-93e4-5f571e91255a/", // Auth v3.2, 1.0 token "https://login.microsoftonline.com/f8cdef31-a31e-4b4a-93e4-5f571e91255a/v2.0", // Auth v3.2, 2.0 token "https://sts.windows.net/cab8a31a-1906-4287-a0d8-4eef66b95f6e/", // Auth for US Gov, 1.0 token "https://login.microsoftonline.us/cab8a31a-1906-4287-a0d8-4eef66b95f6e/v2.0", // Auth for US Gov, 2.0 token }, ValidateAudience = false, // Audience validation takes place manually in code. ValidateLifetime = true, ClockSkew = TimeSpan.FromMinutes(5), RequireSignedTokens = true, }; var tokenExtractor = new JwtTokenExtractor( httpClient, toBotFromEmulatorTokenValidationParameters, _toBotFromEmulatorOpenIdMetadataUrl, AuthenticationConstants.AllowedSigningAlgorithms); var identity = await tokenExtractor.GetIdentityAsync(authHeader, channelId, authConfiguration.RequiredEndorsements).ConfigureAwait(false); if (identity == null) { // No valid identity. Not Authorized. throw new UnauthorizedAccessException("Invalid Identity"); } if (!identity.IsAuthenticated) { // The token is in some way invalid. Not Authorized. throw new UnauthorizedAccessException("Token Not Authenticated"); } // Now check that the AppID in the claimset matches // what we're looking for. Note that in a multi-tenant bot, this value // comes from developer code that may be reaching out to a service, hence the // Async validation. Claim versionClaim = identity.Claims.FirstOrDefault(c => c.Type == AuthenticationConstants.VersionClaim); if (versionClaim == null) { throw new UnauthorizedAccessException("'ver' claim is required on Emulator Tokens."); } string tokenVersion = versionClaim.Value; string appID = string.Empty; // The Emulator, depending on Version, sends the AppId via either the // appid claim (Version 1) or the Authorized Party claim (Version 2). if (string.IsNullOrWhiteSpace(tokenVersion) || tokenVersion == "1.0") { // either no Version or a version of "1.0" means we should look for // the claim in the "appid" claim. Claim appIdClaim = identity.Claims.FirstOrDefault(c => c.Type == AuthenticationConstants.AppIdClaim); if (appIdClaim == null) { // No claim around AppID. Not Authorized. throw new UnauthorizedAccessException("'appid' claim is required on Emulator Token version '1.0'."); } appID = appIdClaim.Value; } else if (tokenVersion == "2.0") { // Emulator, "2.0" puts the AppId in the "azp" claim. Claim appZClaim = identity.Claims.FirstOrDefault(c => c.Type == AuthenticationConstants.AuthorizedParty); if (appZClaim == null) { // No claim around AppID. Not Authorized. throw new UnauthorizedAccessException("'azp' claim is required on Emulator Token version '2.0'."); } appID = appZClaim.Value; } else { // Unknown Version. Not Authorized. throw new UnauthorizedAccessException($"Unknown Emulator Token version '{tokenVersion}'."); } if (!await credentialFactory.IsValidAppIdAsync(appID, cancellationToken).ConfigureAwait(false)) { throw new UnauthorizedAccessException($"Invalid AppId passed on token: {appID}"); } return(identity); }
private async Task GovernmentChannelValidation_ValidateIdentityAsync(ClaimsIdentity identity, ServiceClientCredentialsFactory credentialFactory, string serviceUrl, CancellationToken cancellationToken) { if (identity == null) { // No valid identity. Not Authorized. throw new UnauthorizedAccessException(); } if (!identity.IsAuthenticated) { // The token is in some way invalid. Not Authorized. throw new UnauthorizedAccessException(); } // Now check that the AppID in the claimset matches // what we're looking for. Note that in a multi-tenant bot, this value // comes from developer code that may be reaching out to a service, hence the // Async validation. // Look for the "aud" claim, but only if issued from the Bot Framework var audienceClaim = identity.Claims.FirstOrDefault( c => c.Issuer == _toBotFromChannelTokenIssuer && c.Type == AuthenticationConstants.AudienceClaim); if (audienceClaim == null) { // The relevant audience Claim MUST be present. Not Authorized. throw new UnauthorizedAccessException(); } // The AppId from the claim in the token must match the AppId specified by the developer. // In this case, the token is destined for the app, so we find the app ID in the audience claim. var appIdFromClaim = audienceClaim.Value; if (string.IsNullOrWhiteSpace(appIdFromClaim)) { // Claim is present, but doesn't have a value. Not Authorized. throw new UnauthorizedAccessException(); } if (!await credentialFactory.IsValidAppIdAsync(appIdFromClaim, cancellationToken).ConfigureAwait(false)) { // The AppId is not valid. Not Authorized. throw new UnauthorizedAccessException($"Invalid AppId passed on token: {appIdFromClaim}"); } if (serviceUrl != null) { var serviceUrlClaim = identity.Claims.FirstOrDefault(claim => claim.Type == AuthenticationConstants.ServiceUrlClaim)?.Value; if (string.IsNullOrWhiteSpace(serviceUrlClaim)) { // Claim must be present. Not Authorized. throw new UnauthorizedAccessException(); } if (!string.Equals(serviceUrlClaim, serviceUrl, StringComparison.OrdinalIgnoreCase)) { // Claim must match. Not Authorized. throw new UnauthorizedAccessException(); } } }
public Task <bool> IsValidAppIdAsync(string appId) { return(_credentialFactory.IsValidAppIdAsync(appId, CancellationToken.None)); }