DNV.OAuth.Web.Extensions
1.17.1
dotnet add package DNV.OAuth.Web.Extensions --version 1.17.1
NuGet\Install-Package DNV.OAuth.Web.Extensions -Version 1.17.1
<PackageReference Include="DNV.OAuth.Web.Extensions" Version="1.17.1" />
<PackageVersion Include="DNV.OAuth.Web.Extensions" Version="1.17.1" />
<PackageReference Include="DNV.OAuth.Web.Extensions" />
paket add DNV.OAuth.Web.Extensions --version 1.17.1
#r "nuget: DNV.OAuth.Web.Extensions, 1.17.1"
#:package DNV.OAuth.Web.Extensions@1.17.1
#addin nuget:?package=DNV.OAuth.Web.Extensions&version=1.17.1
#tool nuget:?package=DNV.OAuth.Web.Extensions&version=1.17.1
DNV.OAuth.Web.Extensions
DNV.OAuth.Web.Extensions helps ASP.NET Core applications add optional OAuth web behaviors on top of DNV.OAuth.Web, including API-friendly cookie responses, MFA challenges, path-based multitenancy, and Veracity policy validation.
Overview
This package extends the DNV.OAuth.Web.AddOidc and OidcOptions model. It includes:
- Cookie redirect suppression for APIs.
- MFA request helpers that add
mfa_required=trueto OIDC authorization requests. - Path-based tenant resolution and per-tenant cookie isolation.
- Veracity policy validation during OIDC token validation.
- Public policy validation interfaces for custom validation or violation handling.
Source repository: https://github.com/dnv-internal/SolutionPackage
Package registry: TODO: Confirm the NuGet feed or registry where this package is published.
When to use this
- Use this when a
DNV.OAuth.Webapplication needs one of the optional web authentication behaviors provided here. - Use this when API endpoints should return 401/403 instead of cookie-auth 302 redirects.
- Use this when a web app needs path-based tenant cookie isolation or Veracity policy validation.
When not to use this
- Do not use this without the base
DNV.OAuth.WebOIDC setup. - Do not use path-based multitenancy as a replacement for authorization or tenant data isolation.
- Do not use MFA helpers as proof of strong authentication unless the identity provider issues the expected
mfaTypeclaim.
Features
SuppressOAuthRedirectForApifor 401/403 API responses.AddMfaSupport,ChallengeForMfaAsync, andSignedInWithMfa.UseMultitenancyandAddMultitenantAuthentication.AddPolicyValidationandPolicyValidationOptions.IPolicyValidatorandIPolicyViolationHandlerextension points.- Targets
net8.0andnet10.0.
Requirements
| Requirement | Version / value | Notes |
|---|---|---|
| Runtime | net8.0, net10.0 |
From DNV.OAuth.Web.Extensions.csproj. |
| Base package | DNV.OAuth.Web |
This package extends AddOidc and OidcOptions. |
| Policy dependency | DNV.Veracity.Services.Api.My |
Used by policy validation. |
| Host framework | ASP.NET Core | Uses cookie, OIDC, middleware, and authorization APIs. |
| Platform | TODO | Confirm supported operating systems. |
| Stability | TODO | Confirm package stability and support policy. |
| License | TODO | No repository license file was found. |
Installation
dotnet add package DNV.OAuth.Web.Extensions
If your environment uses a private NuGet feed, configure that feed before running the command.
Quick start
Configure base OIDC with DNV.OAuth.Web, then add the extensions you need.
using DNV.OAuth.Web;
using DNV.OAuth.Web.Extensions.Cookie;
using DNV.OAuth.Web.Extensions.Mfa;
using DNV.OAuth.Web.Extensions.Policy;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddDistributedMemoryCache();
var oidcOptions = new OidcOptions
{
Authority = "https://login.veracity.com/tfp/<tenant>/b2c_1a_signinwithadfsidp/v2.0",
ClientId = "<client-id>",
ClientSecret = Environment.GetEnvironmentVariable("OAUTH_CLIENT_SECRET")!,
Scopes = new[] { "api://example/user_impersonation", "offline_access" },
ResponseType = OpenIdConnectResponseType.Code
};
oidcOptions.AddMfaSupport(request => request.Path.StartsWithSegments("/secure"));
builder.Services.AddOidc(oidcOptions)
.AddPolicyValidation(options =>
{
options.VeracityPolicyApiConfigName = "VeracityMyPolicies";
options.ServiceId = "<service-id>";
options.PolicyValidationMode = PolicyValidationMode.All;
});
builder.Services.SuppressOAuthRedirectForApi(request => request.Path.StartsWithSegments("/api"));
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Expected result:
OIDC sign-in is configured, /secure challenges request MFA, policy validation runs after token validation, and /api redirects become 401/403 responses.
Use cases
Use case: Return 401/403 for API cookie challenges
Goal: Prevent API clients from receiving HTML login redirects.
Scenario: A web app has both MVC pages and /api endpoints protected by cookie authentication.
Prerequisites:
- Cookie authentication configured through
DNV.OAuth.Web.AddOidcor ASP.NET Core cookie authentication.
Code:
using DNV.OAuth.Web.Extensions.Cookie;
var builder = WebApplication.CreateBuilder(args);
builder.Services.SuppressOAuthRedirectForApi(
request => request.Path.StartsWithSegments("/api"));
Expected result:
Unauthenticated /api requests receive 401. Forbidden /api requests receive 403.
Why this works: The extension wraps OnRedirectToLogin and OnRedirectToAccessDenied on CookieAuthenticationOptions.
Common mistakes:
- Passing no predicate suppresses redirects for every request.
- Suppression applies only when the response status is still 200 OK.
Related:
Use case: Request MFA for sensitive routes
Goal: Ask the identity provider for MFA only when the request requires it.
Scenario: A signed-in user accesses /secure and the application wants an MFA challenge when the session does not already show MFA.
Prerequisites:
- OIDC configured with
DNV.OAuth.Web. - Identity provider support for the
mfa_requiredparameter andmfaTypeclaim values used by this package.
Code:
using DNV.OAuth.Web;
using DNV.OAuth.Web.Extensions.Mfa;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDistributedMemoryCache();
var oidcOptions = new OidcOptions
{
Authority = "https://login.veracity.com/tfp/<tenant>/b2c_1a_signinwithadfsidp/v2.0",
ClientId = "<client-id>",
ClientSecret = Environment.GetEnvironmentVariable("OAUTH_CLIENT_SECRET")!,
Scopes = new[] { "api://example/user_impersonation" }
};
oidcOptions.AddMfaSupport(request => request.Path.StartsWithSegments("/secure"));
builder.Services.AddOidc(oidcOptions);
public sealed class SecureController : Controller
{
public async Task<IActionResult> Index()
{
if (!HttpContext.SignedInWithMfa())
{
await HttpContext.ChallengeForMfaAsync("/secure");
return new EmptyResult();
}
return Content("MFA session");
}
}
Expected result:
OIDC redirects for /secure include mfa_required=true until the signed-in principal has an accepted mfaType claim.
Why this works: AddMfaSupport wraps OnRedirectToIdentityProvider; ChallengeForMfaAsync stores the MFA flag in AuthenticationProperties.
Common mistakes:
- Calling
ChallengeForMfaAsyncwith an empty redirect URL. - Assuming
SignedInWithMfais true for all MFA mechanisms; it checksmfaType=phoneormfaType=federatedIdp.
Related:
Use case: Isolate authentication cookies by tenant path
Goal: Use the first URL path segment as a tenant alias and isolate authentication cookies.
Scenario: The same app serves /tenant-a and /tenant-b, but a user session for one tenant should not be reused by the other tenant.
Prerequisites:
- OIDC configured with
DNV.OAuth.Web. UseMultitenancycalled before routing and authentication.
Code:
using DNV.OAuth.Web;
using DNV.OAuth.Web.Extensions.Multitenancy;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDistributedMemoryCache();
builder.Services.AddOidc(new OidcOptions
{
Authority = "https://login.veracity.com/tfp/<tenant>/b2c_1a_signinwithadfsidp/v2.0",
ClientId = "<client-id>",
ClientSecret = Environment.GetEnvironmentVariable("OAUTH_CLIENT_SECRET")!,
Scopes = new[] { "api://example/user_impersonation", "offline_access" }
}).AddMultitenantAuthentication();
var app = builder.Build();
app.UseMultitenancy(path => path.StartsWithSegments("/health"));
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Expected result:
Requests such as /tenant-a/orders run with PathBase=/tenant-a, and cookie names receive tenant-aware postfixes.
Why this works: TenantResolutionMiddleware moves the first path segment to PathBase, while multitenant cookie options append .Super or .Sub postfixes.
Common mistakes:
- Calling
UseMultitenancyafterUseRoutingorUseAuthentication. - Treating path tenant resolution as data authorization. Always validate tenant access in application logic.
Related:
Use case: Validate Veracity policies during sign-in
Goal: Require platform, service, or subscription policy validation as part of OIDC sign-in.
Scenario: A web app must ensure Veracity policies are accepted before the user is authorized.
Prerequisites:
DNV.Veracity.Services.Api.Myconfiguration for the named policy API config.- A policy mode appropriate for the application.
Code:
using DNV.OAuth.Web;
using DNV.OAuth.Web.Extensions.Policy;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDistributedMemoryCache();
builder.Services.AddOidc(new OidcOptions
{
Authority = "https://login.veracity.com/tfp/<tenant>/b2c_1a_signinwithadfsidp/v2.0",
ClientId = "<client-id>",
ClientSecret = Environment.GetEnvironmentVariable("OAUTH_CLIENT_SECRET")!,
Scopes = new[] { "api://example/user_impersonation", "offline_access" }
}).AddPolicyValidation(options =>
{
options.VeracityPolicyApiConfigName = "VeracityMyPolicies";
options.ServiceId = "<service-id>";
options.PolicyValidationMode = PolicyValidationMode.All;
options.AuthorizationPolicyName = PolicyValidationOptions.VeracityDefaultPolicy;
options.AddAsDefaultPolicy = true;
});
Expected result:
Successful validation adds the vplcvdt claim and the configured authorization policy requires that claim.
Why this works: AddPolicyValidation wraps the OIDC OnTokenValidated event and registers an authorization policy requiring vplcvdt.
Common mistakes:
- Leaving
PolicyValidationModeat the default value0; validation fails because no mode is selected. - Omitting
VeracityPolicyApiConfigName;AddPolicyValidationthrowsArgumentNullException. - Omitting
ServiceIdwhen using modes that validate service policies.
Related:
Use case: Override policy violation handling
Goal: Customize how policy validation failures are handled.
Scenario: The default handler redirects to the URL returned by the policy API, but an application wants a 403 response.
Prerequisites:
- A custom
IPolicyViolationHandlerregistered beforeAddPolicyValidation.
Code:
using DNV.OAuth.Web.Extensions.Policy;
using DNV.Veracity.Services.Api.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IPolicyViolationHandler, ForbiddenPolicyViolationHandler>();
public sealed class ForbiddenPolicyViolationHandler : IPolicyViolationHandler
{
public Task HandleTermsAndConditionsViolated<TOptions>(
RemoteAuthenticationContext<TOptions> ctx,
PolicyValidationResult validationResult)
where TOptions : AuthenticationSchemeOptions
=> WriteForbidden(ctx);
public Task HandleServiceSubscriptionViolated<TOptions>(
RemoteAuthenticationContext<TOptions> ctx,
PolicyValidationResult validationResult)
where TOptions : AuthenticationSchemeOptions
=> WriteForbidden(ctx);
public Task HandleCompanyAffiliationViolated<TOptions>(
RemoteAuthenticationContext<TOptions> ctx,
PolicyValidationResult validationResult)
where TOptions : AuthenticationSchemeOptions
=> WriteForbidden(ctx);
private static Task WriteForbidden<TOptions>(RemoteAuthenticationContext<TOptions> ctx)
where TOptions : AuthenticationSchemeOptions
{
ctx.HandleResponse();
ctx.Response.StatusCode = StatusCodes.Status403Forbidden;
return ctx.Response.WriteAsync("Policy validation failed.");
}
}
Expected result:
Policy violations return 403 with a custom message instead of using the default redirect behavior.
Why this works: The package registers default policy services with TryAddSingleton, so earlier registrations win.
Common mistakes:
- Registering the custom handler after
AddPolicyValidation. - Writing sensitive policy API details into the response.
Related:
Configuration
| Option | Type | Default | Required | Description |
|---|---|---|---|---|
SuppressOAuthRedirectForApi(apiPredicate) |
Func<HttpRequest,bool>? |
Suppress all when null | No | Selects requests that should receive 401/403 instead of redirects. |
AddMfaSupport(mfaPredict) |
Func<HttpRequest,bool>? |
Uses challenge property when null | No | Selects requests that should add mfa_required=true. |
UseMultitenancy(shouldSkip) |
Func<PathString,bool>? |
Do not skip | No | Skips tenant extraction for selected paths. |
AddMultitenantAuthentication(configAction) |
Action<CookieAuthenticationOptions,HttpContext>? |
null |
No | Customizes per-request cookie options. |
PolicyValidationOptions.PolicyValidationMode |
PolicyValidationMode |
0 |
Yes | Selects platform/service/subscription checks. |
PolicyValidationOptions.VeracityPolicyApiConfigName |
string? |
null |
Yes | Name passed to AddMyPolicies. |
PolicyValidationOptions.ServiceId |
string? |
null |
Some modes | Service id for service policy validation. |
PolicyValidationOptions.GetReturnUrl |
Func<HttpContext,string,string>? |
Builds absolute URL | No | Custom return URL builder. |
PolicyValidationOptions.AuthorizationPolicyName |
string |
VeracityDefaultPolicy |
Yes | Authorization policy requiring vplcvdt. |
PolicyValidationOptions.AddAsDefaultPolicy |
bool |
true |
No | Sets the policy as default. |
API overview
| API | Purpose | Typical use |
|---|---|---|
SuppressOAuthRedirectForApi |
Converts cookie redirects to 401/403. | API endpoints in cookie-auth apps. |
AddMfaSupport |
Adds MFA request behavior to OidcOptions. |
Sensitive routes or explicit MFA challenge. |
ChallengeForMfaAsync |
Starts an MFA challenge. | Controller/action requiring MFA. |
SignedInWithMfa |
Checks the mfaType claim. |
Gate sensitive actions after sign-in. |
UseMultitenancy |
Resolves first path segment as tenant alias. | Path-based tenant routing. |
AddMultitenantAuthentication |
Configures tenant-aware cookies and OIDC callback handling. | Tenant-isolated authentication sessions. |
AddPolicyValidation |
Adds Veracity policy checks and an authorization policy. | Sign-in policy enforcement. |
IPolicyValidator |
Policy validation abstraction. | Replace validation behavior. |
IPolicyViolationHandler |
Policy violation abstraction. | Replace redirect/error behavior. |
PolicyValidationMode |
Flags for policy validation. | Select platform, service, and subscription checks. |
Error handling and troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| API still receives 302 redirects | Predicate did not match the API path. | Check the apiPredicate passed to SuppressOAuthRedirectForApi. |
| MFA parameter is not sent | Predicate returned false and no challenge property was set. | Use AddMfaSupport with the right predicate or call ChallengeForMfaAsync. |
| Tenant path is not resolved | Middleware order is wrong or shouldSkip matched. |
Call UseMultitenancy before routing/authentication and review skip rules. |
HttpContext is not available. |
Tenant cookie options were created outside a request. | Use multitenant cookie options only in request-aware paths. |
| Policy validation always fails | PolicyValidationMode is 0 or required options are missing. |
Set a valid mode, API config name, and service id when required. |
Observability and diagnostics
PolicyValidator logs Veracity policy API errors with ILogger<PolicyValidator>. Other extensions rely on ASP.NET Core authentication and middleware logging.
Performance considerations
- Multitenancy creates cookie options per request through a custom
IOptionsMonitor<CookieAuthenticationOptions>. - Policy validation calls the Veracity policy API during OIDC token validation.
- Cookie suppression and MFA event wrappers add minimal per-request overhead.
- TODO: Confirm trimming and native AOT support.
Security notes
- Keep OIDC client secrets and Veracity API configuration secrets out of source control.
- MFA helpers request MFA and inspect specific
mfaTypeclaim values; they do not replace server-side authorization checks. - Path-based tenant resolution isolates authentication cookies but does not enforce tenant data access. Validate tenant authorization in application code.
- Policy validation may redirect users to URLs returned by the policy API. Validate configuration and avoid exposing sensitive details in custom handlers.
- Authentication cookies and policy validation responses can reveal user state. Use HTTPS, secure cookies, and appropriate cache controls.
- Vulnerability reporting: TODO: Confirm the security contact or
SECURITY.mdprocess for this repository.
Examples
Versioning and compatibility
The project targets net8.0 and net10.0. Package versioning is controlled by repository MSBuild properties and BUILD_VERSION.
TODO: Confirm semantic versioning policy, supported package versions, and breaking-change process.
Contributing
From the repository root:
dotnet test src/OAuth/OAuth.sln
TODO: Confirm contribution guidelines and code review requirements.
Support
Package metadata lists author Albert Yang <albert.yang@dnv.com>.
TODO: Confirm the owning team, support contact, and issue reporting process.
License
TODO: Confirm the package license and add or link the repository license file.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. 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 was computed. 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. |
-
net6.0
- DNV.OAuth.Web (>= 1.17.1)
- DNV.Veracity.Services.Api.My (>= 1.5.3)
-
net8.0
- DNV.OAuth.Web (>= 1.17.1)
- DNV.Veracity.Services.Api.My (>= 1.5.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.17.1 | 59,563 | 2/27/2026 |
| 1.17.1-preview.1789020820 | 57 | 9/10/2026 |
| 1.17.1-preview.1785316862 | 77 | 7/29/2026 |
| 1.17.0 | 150 | 2/26/2026 |
| 1.16.0 | 148 | 2/26/2026 |
| 1.15.1 | 1,771 | 2/3/2026 |
| 1.15.1-preview.1772090606 | 94 | 2/26/2026 |
| 1.15.1-preview.1771954705 | 87 | 2/24/2026 |
| 1.15.1-preview.1771953954 | 85 | 2/24/2026 |
| 1.15.0 | 342 | 4/30/2025 |
| 1.14.7 | 5,925 | 4/16/2025 |
| 1.14.5 | 336 | 3/12/2025 |
| 1.14.4 | 131,616 | 11/27/2024 |
| 1.14.3 | 288 | 11/27/2024 |
| 1.14.2 | 5,056 | 10/9/2024 |
| 1.14.1 | 278 | 9/27/2024 |
| 1.14.0 | 265 | 9/26/2024 |
| 1.13.0 | 24,450 | 8/9/2024 |