Resource Protection using JWT in ASP.NET Core
This library is a supplement to ASP.NET Core Security, adding functionality that helps you use Cloud Foundry Security services such as Single Sign-On for VMware Tanzu or a UAA Server for authentication and authorization using JSON Web Tokens (JWT) in ASP.NET Core web applications.
For more information, see also the Steeltoe Security samples.
General guidance for use of JWT is beyond the scope of this document. For information, see:
For information about the underlying Microsoft JWT Bearer Authentication library, see the Microsoft documentation.
Using JWT in ASP.NET Core
To use this library:
- Add NuGet references.
- Configure settings for the security provider.
- Add and use the security provider in the application.
- Secure your endpoints.
- Create an instance of a Cloud Foundry Single Sign-On service and bind it to your application.
Add NuGet References
To use this package, add NuGet package references to:
Steeltoe.Security.Authentication.JwtBearer
Steeltoe.Configuration.CloudFoundry
(so that Cloud Foundry service bindings can be read by Steeltoe)
Configure Settings
The Steeltoe JWT Bearer library configures the Microsoft JWT Bearer implementation, so all supported settings are available in Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions
.
JwtBearerOptions
is bound to configuration values at the key Authentication:Schemes:Bearer
. The following example appsettings.json
shows how to declare the audience for which tokens should be considered valid (such as when a token is issued to a specific web application and then passed to backend services to perform actions on behalf of a user):
{
"Authentication": {
"Schemes": {
"Bearer": {
"ValidAudience": "sampleapi"
}
}
}
}
Cloud Foundry Service Bindings
The Steeltoe package Steeltoe.Configuration.CloudFoundry
reads Single Sign-On credentials from Cloud Foundry service bindings (VCAP_SERVICES
) and re-maps them for Microsoft's JWT Bearer library to read. Add the configuration provider to your application with this code:
using Steeltoe.Configuration.CloudFoundry;
using Steeltoe.Configuration.CloudFoundry.ServiceBindings;
var builder = WebApplication.CreateBuilder(args);
// Steeltoe: Add Cloud Foundry application and service info to configuration.
builder.AddCloudFoundryConfiguration();
builder.Configuration.AddCloudFoundryServiceBindings();
Local UAA
A UAA server, such as UAA Server for Steeltoe, can be used for local development of applications that will be deployed to Cloud Foundry. Configuration of UAA itself is beyond the scope of this topic, but configuring your appsettings.Development.json
to work with a local UAA server is possible with the addition of settings like these:
{
"Authentication": {
"Schemes": {
"Bearer": {
"Authority": "http://localhost:8080/uaa",
"ClientId": "steeltoesamplesserver",
"ClientSecret": "server_secret",
"MetadataAddress": "http://localhost:8080/.well-known/openid-configuration",
"RequireHttpsMetadata": false
}
}
}
}
Important
If you want to use the Steeltoe UAA server without modification, some application configuration options will be limited. The Steeltoe UAA Server configuration is available in uaa.yml.
Add and use JWT Bearer Authentication
Most of the JWT Bearer functionality is provided by the Microsoft libraries, so the only difference when using Steeltoe is the addition of calling ConfigureJwtBearerForCloudFoundry
on the AuthenticationBuilder
, as shown in the following example:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Steeltoe.Security.Authentication.JwtBearer;
// Add Microsoft Authentication services
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer()
// Steeltoe: configure JWT to work with UAA/Cloud Foundry
.ConfigureJwtBearerForCloudFoundry();
// Register Microsoft Authorization services
builder.Services.AddAuthorizationBuilder()
// Create a named authorization policy that requires the user to have a scope with the same value
.AddPolicy("sampleapi.read", policy => policy.RequireClaim("scope", "sampleapi.read"));
The middleware pipeline order is important: Activate authentication and authorization services after routing services, but before controller route registrations, as shown in the following example.
var app = builder.Build();
app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedHost | ForwardedHeaders.XForwardedProto });
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapDefaultControllerRoute();
app.Run();
Note
In this sample code, app.UseForwardedHeaders
is used so that any links generated within the application are compatible with reverse-proxy scenarios, such as when running in Cloud Foundry.
Securing Endpoints
After the services and middleware have been configured, you can secure endpoints with the standard ASP.NET Core Authorize
attribute, as follows:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[Route("api/[controller]")]
[ApiController]
public class SampleController : ControllerBase
{
[HttpGet]
[Authorize(Policy = "sampleapi.read")]
public string Get()
{
return "You have permission to read from the sample API.";
}
}
In the preceding example, if an incoming GET request is made to the /api/sample
endpoint and the request does not contain a valid JWT bearer token for a user with a scope
claim equal to sampleapi.read
, the request is rejected.
See the Steeltoe Security samples for examples using a user's access token to interact with downstream APIs, focusing on these locations:
- Configure ASP.NET Core to save the user's token
- Get the user's token
- Include the token in a downstream request
Single Sign-On for VMware Tanzu
When using Single Sign-On for VMware Tanzu, you must choose a service plan before you create a service instance. If you do not have an existing service plan, a platform operator might need to create a new plan for you. For information about configuring service plans for use by developers, a platform operator should follow the steps in the Single Sign-On for Tanzu operator guide.
After you have identified the service plan to use, create a service instance:
cf create-service p-identity SERVICE_PLAN_NAME MY_SERVICE_INSTANCE
Bind and configure with app manifest
Using a manifest file when you deploy to Cloud Foundry is recommended, and can save some work with the SSO configuration. For information about configuring an app manifest, see the Single Sign-On documentation.
Consider this example manifest that names the application and buildpack, and configures properties for the SSO binding:
applications:
- name: steeltoesamplesserver
buildpacks:
- dotnet_core_buildpack
env:
GRANT_TYPE: client_credentials
SSO_AUTHORITIES: uaa.resource, sampleapi.read
SSO_RESOURCES: sampleapi.read
SSO_SHOW_ON_HOME_PAGE: false
services:
- sampleSSOService
Bind and configure manually
Alternatively, you can bind the instance manually and restage the app with the Cloud Foundry CLI. Then you can configure the SSO binding with the web interface:
# Bind service to your app
cf bind-service MY_APPLICATION MY_SERVICE_INSTANCE
# Restage the app to pick up change
cf restage MY_APPLICATION
For more information, see:
UAA Server
If Single Sign-On for Tanzu is not available or desired for your application, you can use UAA.
There is no service broker available to manage service instances or bindings for UAA, so you must have a user provided service instance to hold the credentials.
This command is an example of how the service instance can be created:
cf cups MY_SERVICE_INSTANCE -p '{"auth_domain": "https://uaa.login.sys.cf-app.com","grant_types": [ "authorization_code", "client_credentials" ],"client_secret": "SOME_CLIENT_SECRET","client_id": "SOME_CLIENT_ID"}'
And to bind the service instance to the app:
cf bind-service MY_APPLICATION MY_SERVICE_INSTANCE
For additional information, see the UAA documentation.