EbrahimMansur.Dpop.AspNetCore
0.1.1
dotnet add package EbrahimMansur.Dpop.AspNetCore --version 0.1.1
NuGet\Install-Package EbrahimMansur.Dpop.AspNetCore -Version 0.1.1
<PackageReference Include="EbrahimMansur.Dpop.AspNetCore" Version="0.1.1" />
<PackageVersion Include="EbrahimMansur.Dpop.AspNetCore" Version="0.1.1" />
<PackageReference Include="EbrahimMansur.Dpop.AspNetCore" />
paket add EbrahimMansur.Dpop.AspNetCore --version 0.1.1
#r "nuget: EbrahimMansur.Dpop.AspNetCore, 0.1.1"
#:package EbrahimMansur.Dpop.AspNetCore@0.1.1
#addin nuget:?package=EbrahimMansur.Dpop.AspNetCore&version=0.1.1
#tool nuget:?package=EbrahimMansur.Dpop.AspNetCore&version=0.1.1
EbrahimMansur.Dpop.AspNetCore
ASP.NET Core integration for EbrahimMansur.Dpop,
implementing DPoP (RFC 9449) sender-constrained access tokens on top of your existing JWT Bearer
authentication — without replacing or duplicating it.
Install
dotnet add package EbrahimMansur.Dpop.AspNetCore
5-minute setup
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(/* your existing configuration */);
builder.Services.AddDpop(options =>
{
options.Mode = DpopMode.Optional; // existing Bearer-only endpoints are unaffected
});
var app = builder.Build();
// Required for correct `htu` validation behind a reverse proxy — see "Reverse proxies" below.
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost,
});
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/orders", () => Results.Ok(new[] { "order-1" }))
.RequireAuthorization()
.RequireDpop();
app.MapControllers();
app.Run();
[Authorize]
[RequireDpop]
public class OrdersController : ControllerBase
{
[HttpGet]
public IActionResult Get() => Ok(new[] { "order-1" });
}
Installing the package and calling AddDpop() alone does not require DPoP on any endpoint —
existing Bearer-only endpoints keep working unchanged. DPoP is enforced only where you explicitly
add .RequireDpop() or [RequireDpop], or when you set DpopOptions.Mode = DpopMode.Required
globally.
How it works
The DPoP requirement is implemented as an ASP.NET Core authorization requirement, running after your
existing authentication scheme (e.g. JWT Bearer) has already populated HttpContext.User. DPoP
validation never touches or replaces token authentication — it only adds an extra proof-of-possession
check on top of it, reading the access token's cnf claim to bind the proof's key to the token.
Failures are translated into a 401 response with a WWW-Authenticate: DPoP ... challenge header
(and, when nonce enforcement is enabled, a DPoP-Nonce header) instead of the framework's default
403. Ordinary authorization failures (e.g. missing roles) are unaffected.
Reverse proxies
DPoP validates the request's htu (HTTP URI) against the externally visible URI, not whatever
Kestrel sees internally. Behind a reverse proxy (NGINX, Kong, an API gateway, Kubernetes ingress),
you must configure UseForwardedHeaders with a KnownProxies/KnownNetworks scoped to your actual
trusted proxy — never left as an open wildcard — so HttpContext.Request.Scheme/Host reflect the
client-facing values before DPoP validation runs.
Distributed deployments
The default IDpopReplayStore/IDpopNonceStore are in-memory and only correct for a single API
instance. Once the API is horizontally scaled, register a distributed implementation of
IDpopReplayStore/IDpopNonceStore (e.g. Redis- or IDistributedCache-backed) instead — the
interfaces are designed to make this a drop-in replacement.
Source, issues, and contributing
Source code, issue tracker, and contributing guidelines live at github.com/ebrahimmansur/dpop-dotnet. Found a bug or unexpected HTTP status/header? Open an issue with a minimal repro, your package version, and the actual request/response you saw.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- EbrahimMansur.Dpop (>= 0.1.1)
-
net9.0
- EbrahimMansur.Dpop (>= 0.1.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.