Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
the validator checks whitelist and decodes token and compare this with appId claim. An AsyncHelper invoke asynchronous method synchronously. A startup class is a normal .net core startup class
// Copyright (c) Microsoft Corporation, Inc. All rights reserved.
// Licensed under the MIT License, Version 2.0. See License.txt in the project root for license information.
/// <summary>
/// Helper created by microsoft
/// https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs
/// </summary>
public class AsyncHelper
{
private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None,
TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
var cultureUi = CultureInfo.CurrentUICulture;
var culture = CultureInfo.CurrentCulture;
return _myTaskFactory.StartNew(() =>
{
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = cultureUi;
return func();
}).Unwrap().GetAwaiter().GetResult();
}
public static void RunSync(Func<Task> func)
{
var cultureUi = CultureInfo.CurrentUICulture;
var culture = CultureInfo.CurrentCulture;
_myTaskFactory.StartNew(() =>
{
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = cultureUi;
return func();
}).Unwrap().GetAwaiter().GetResult();
}
}
public class Startup
{
private readonly IConfiguration _configuration;
public Startup(IConfiguration configuration)
{
_configuration = configuration;
}
/// <summary>
/// whitelist variable gets a section called Whitelist and takes all values
/// AzureAd:Instance common is a https://login.microsoftonline.com/
/// AzureAd:TenantId is tenant Id or tenant name, or if it is a public azure ad application that can be common too. more details here : https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-convert-app-to-be-multi-tenant
/// </summary>
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddAuthentication(cfg =>
{
cfg.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
cfg.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
_configuration.Bind("AzureAd", options);
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
ValidateIssuer = false
};
//
var whiteList = _configuration.
GetSection("Whitelist")
.AsEnumerable()
.Where(s => string.IsNullOrEmpty(s.Value) == false)
.Select(s =>
{
if (Guid.TryParse(s.Value, out var g))
{
return g;
}
return Guid.Empty;
})
.Where(g => g != Guid.Empty)
.ToArray();
options.SecurityTokenValidators.Add(new WhitelistAppTokenValidation(_configuration["AzureAd:Instance"], _configuration["AzureAd:TenantId"], whiteList));
options.Validate();
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication();
app.UseMvc();
}
}
public class WhitelistAppTokenValidation : ISecurityTokenValidator
{
private readonly string _tenatId;
private readonly string _authority;
private readonly Guid[] _whitelist;
public WhitelistAppTokenValidation(string authority, string tenatId, params Guid[] whiteList)
{
_tenatId = string.IsNullOrWhiteSpace(tenatId) == false ? tenatId : throw new ArgumentException(nameof(tenatId));
_authority = string.IsNullOrWhiteSpace(authority) == false ? authority : throw new ArgumentException(nameof(authority));
_whitelist = whiteList != null && whiteList.Any() ? whiteList : throw new ArgumentException(nameof(ArgumentException));
}
public bool CanReadToken(string securityToken) => true;
public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
{
string stsDiscoveryEndpoint = $"{_authority}/{_tenatId}/v2.0/.well-known/openid-configuration";
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
var config = AsyncHelper.RunSync(configManager.GetConfigurationAsync);
validationParameters.IssuerSigningKeys = config.SigningKeys;
var validationIssuerOldValue = validationParameters.ValidateIssuer;
var validateAudiencedOldValue = validationParameters.ValidateAudience;
validationParameters.ValidateIssuer = false;
validationParameters.ValidateAudience = false;
var tokenHandler = new JwtSecurityTokenHandler();
var result = tokenHandler.ValidateToken(securityToken, validationParameters, out validatedToken);
var claimValue = result.Claims.GetEnumerator();
var appId = Guid.Empty;
if (claimValue != null)
{
while (claimValue.MoveNext())
{
if (claimValue.Current.Type == "appid")
{
var aId = claimValue.Current.Value;
if (Guid.TryParse(aId, out appId) == false)
{
throw new InvalidCastException($"Cannot cast string {aId} to guid");
}
break;
}
}
}
if (appId == Guid.Empty)
{
throw new UnauthorizedException($"Token doesn't have application Id");
}
if (_whitelist.Contains(appId) == false)
{
throw new UnauthorizedException($"An application Id {appId} isn't being in whitelist");
}
validationParameters.ValidateIssuer = validationIssuerOldValue;
validationParameters.ValidateAudience = validateAudiencedOldValue;
return result;
}
public bool CanValidateToken { get; } = true;
public int MaximumTokenSizeInBytes { get; set; }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.