Securing a Web API using multiple token servers

This article shows how a single secure Web API could be used together with multiple secure token servers. The API uses JWT Bearer token authentication, but because the access token come from different token servers, the tokens validation need to be changed.

Code: https://github.com/damienbod/ApiJwtWithTwoSts

Using multiple Authorities with shared certitficate

The first way this can be supported, is that the Authority option is removed from the AddJwtBearer code configuration. When this is removed, the JWT Bearer has no way of validating the Issuer signing. The same certificate which signed the access token, needs to be used in the API as well as the token server. This is easy if you have control of all the applications and can read the certificate from a shared resource like Azure Key Vault. Multiple Issuers can then be supported by configuring the TokenValidationParameters where the signing key is also coded.

public void ConfigureServices(IServiceCollection services)
{
	var x509Certificate2 = GetCertificate(_environment);

	services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
	   .AddJwtBearer(options =>
		{
			options.Audience = "ProtectedApiResource";
			options.TokenValidationParameters = new TokenValidationParameters
			{
				ValidateIssuer = true,
				ValidIssuers = new List<string> { "https://localhost:44318", "https://localhost:44367" },
				ValidateIssuerSigningKey = true,
				IssuerSigningKey = new X509SecurityKey(x509Certificate2),
				IssuerSigningKeyResolver =
				(string token, SecurityToken securityToken, string kid, TokenValidationParameters validationParameters)
				  => new List<X509SecurityKey> { new X509SecurityKey(x509Certificate2) }
			};
		});

	services.AddAuthorization(options =>
		options.AddPolicy("protectedScope", policy =>
		{
			policy.RequireClaim("scope", "scope_used_for_api_in_protected_zone");
		})
	);

	services.AddControllers();
}

Using multiple Schemes

A different solution would be to use multiple Authentication schemes, a different one for each token service. The Schemes are then added to the default authorization policy. The example shown underneath requires a scope claim scope_used_for_api_in_protected_zone.

public void ConfigureServices(IServiceCollection services)
{
	var x509Certificate2 = GetCertificate(_environment);

	services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
		.AddJwtBearer("JWT", options =>
		{
			options.Audience = "ProtectedApiResourceOne";
			options.Authority = "https://localhost:44318";
		})
		.AddJwtBearer("Custom", options =>
		{
			options.Audience = "ProtectedApiResourceTwo";
			options.Authority = "https://localhost:44367";
		});

	services.AddAuthorization(options =>
	{
		options.DefaultPolicy = new AuthorizationPolicyBuilder()
			.RequireAuthenticatedUser()
			.AddAuthenticationSchemes("JWT", "Custom")
			.Build();

		options.AddPolicy("protectedScope", policy =>
		{
			// scope is required in token from both servers
			policy.RequireClaim("scope", "scope_used_for_api_in_protected_zone");
		});
	  });

	services.AddControllers();
}

Deploy two seperate Web APIs

My favourite solution would be to deploy 2 separate APIs each using a different token service. This would require two separate deployments.

All solutions have advantages, and disadvantages.

Links:

https://docs.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme

https://docs.microsoft.com/en-us/dotnet/framework/security/json-web-token-handler

One comment

  1. […] Securing a Web API using multiple token servers (Damien Bowden) […]

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.