As microservices and API-driven architectures continue to rise in popularity, securing these services becomes increasingly crucial. One of the key features developers need in their API gateways is the ability to manage authentication and authorization.
In this post, we’ll explore how you can implement authentication and authorization using YARP (Yet Another Reverse Proxy) in a .NET 8 Minimal API setup. We'll show you how to handle tokens like OAuth 2.0 and JWT, ensuring that your API proxy acts as a secure gateway between your clients and downstream services.
Why Implement Authentication and Authorization in YARP?
When building a centralized API proxy, you often want to manage authentication and authorization in a unified way. By handling these tasks at the proxy level, you can:
- Centralize Security: Apply consistent security policies across multiple APIs.
- Simplify Backend Services: Let the proxy handle authentication and authorization, so backend services can focus on business logic.
- Token Forwarding: Forward user tokens to downstream APIs, maintaining authorization across services.
Let’s explore how to set this up with YARP in a .NET 8 Minimal API.
Step 1: Setting Up Your YARP Proxy
First, we need to set up a basic YARP proxy in a .NET 8 Minimal API. Create a new project and add the YARP package:
dotnet new web -n AuthYarpProxy
cd AuthYarpProxy
dotnet add package Yarp.ReverseProxy
In the Program.cs
file, configure the basic YARP proxy:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddReverseProxy()
.LoadFromMemory(GetRoutes(), GetClusters());
var app = builder.Build();
app.MapReverseProxy();
app.Run();
static IEnumerable<RouteConfig> GetRoutes()
{
return new[]
{
new RouteConfig
{
RouteId = "secured-route",
ClusterId = "secured-cluster",
Match = new RouteMatch
{
Path = "/api/{**catch-all}"
}
}
};
}
static IEnumerable<ClusterConfig> GetClusters()
{
return new[]
{
new ClusterConfig
{
ClusterId = "secured-cluster",
Destinations = new Dictionary<string, DestinationConfig>
{
{ "api", new DestinationConfig { Address = "https://your-backend-api.com/" } }
}
}
};
}
This sets up a simple YARP proxy that forwards requests to a backend API.
Step 2: Adding Authentication with OAuth 2.0 and JWT
To add authentication, we’ll use OAuth 2.0 and JWT tokens. Let’s assume you are using an identity provider like IdentityServer or Azure AD to issue tokens.
Install Required Packages
We need to add authentication support in .NET:
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Configuring JWT Authentication
Now, configure the YARP proxy to authenticate requests using the Authorization
header with a JWT. Add this configuration in Program.cs
:
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://your-identity-provider.com";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false // depending on your setup
};
});
builder.Services.AddAuthorization();
This tells .NET to use JWT Bearer authentication and validate the tokens issued by your identity provider. The Authority
should point to your token provider (e.g., IdentityServer, Azure AD).
Securing the YARP Proxy
Next, enforce authentication for requests coming to your YARP proxy. You can do this by applying authentication and authorization middleware:
app.UseAuthentication();
app.UseAuthorization();
app.MapReverseProxy().RequireAuthorization();
This ensures that any request to your proxy must include a valid JWT token in the Authorization
header.
Step 3: Forwarding Tokens to Downstream APIs
In many cases, you need to pass the authentication token received from the client to your downstream API. This is crucial when the backend APIs also require authorization based on the same token.
To forward the token, modify the YARP request headers using transforms:
builder.Services.AddReverseProxy()
.LoadFromMemory(GetRoutes(), GetClusters())
.AddTransforms(transforms =>
{
transforms.AddRequestTransform(async context =>
{
if (context.HttpContext.User.Identity.IsAuthenticated)
{
// Extract the JWT token from the incoming request
var token = await context.HttpContext.GetTokenAsync("access_token");
// Add the token to the outgoing request headers
if (!string.IsNullOrEmpty(token))
{
context.ProxyRequest.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
}
}
});
});
Explanation:
- GetTokenAsync("access_token"): This retrieves the JWT token from the incoming request.
- context.ProxyRequest.Headers.Authorization: Adds the token to the outgoing request, so the downstream API can validate it.
Step 4: Testing Authentication and Token Forwarding
With authentication and token forwarding in place, it’s time to test the setup.
You can use a tool like curl to make a request with a JWT token:
curl -H "Authorization: Bearer YOUR_TOKEN_HERE" http://localhost:5000/api/some-endpoint
This request will be authenticated by the YARP proxy, and the token will be forwarded to the downstream API.
Step 5: Custom Authorization Policies
You can also define custom authorization policies to control access to specific routes or actions based on claims, roles, or other token attributes.
For example, you can define a policy that requires a specific role:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy =>
policy.RequireClaim("role", "admin"));
});
app.MapReverseProxy().RequireAuthorization("AdminOnly");
This ensures that only users with the admin
role can access the proxied API.
Conclusion
By integrating authentication and authorization in your YARP proxy, you can create a secure gateway that validates incoming requests and forwards them to backend services. With support for OAuth 2.0, JWT tokens, and token forwarding, YARP enables you to centralize security for your APIs, simplifying the architecture of your backend services.
YARP’s flexibility with authentication transforms makes it a powerful tool for building secure and scalable API gateways.