Dloizides.DirectGrant.AspNetCore 1.0.1

dotnet add package Dloizides.DirectGrant.AspNetCore --version 1.0.1
                    
NuGet\Install-Package Dloizides.DirectGrant.AspNetCore -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Dloizides.DirectGrant.AspNetCore" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Dloizides.DirectGrant.AspNetCore" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Dloizides.DirectGrant.AspNetCore" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Dloizides.DirectGrant.AspNetCore --version 1.0.1
                    
#r "nuget: Dloizides.DirectGrant.AspNetCore, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Dloizides.DirectGrant.AspNetCore@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Dloizides.DirectGrant.AspNetCore&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Dloizides.DirectGrant.AspNetCore&version=1.0.1
                    
Install as a Cake Tool

Dloizides.DirectGrant.AspNetCore

A tiny shared-secret gate for "external direct-grant" internal verify endpoints.

The pattern this supports

A custom Keycloak direct-grant authenticator lets one realm flow accept a non-password credential (email OTP, event-staff PIN, …) by calling back to the owning service over the in-cluster network to verify it:

KC direct-grant authenticator  ──POST {realm, …credential}──▶  /api/v1/auth/internal/verify-{method}
        (X-{Method}-Internal-Key: <shared secret>)                     │
                                                                       ▼
                                                          service verifies + returns {valid,…}

Every such callback endpoint must defend its AllowAnonymous route with a shared-secret check. That check is the one thing they all duplicate — and it is security-critical, so it should exist once, audited.

What it provides

  • AddExternalDirectGrant(name, configure) — registers a named ExternalDirectGrantOptions (header name + shared secret) per method, so several methods ("otp", "pin", …) coexist in one service. The gate service is registered idempotently.
  • AddExternalDirectGrant(name, configuration, sectionName, headerName) — convenience overload that binds the secret from {sectionName}:InternalApiKey (reloadable).
  • IExternalDirectGrantGate.IsAuthorized(httpContext, name) — a constant-time, fail-closed header check. Returns false (caller issues a bodyless 401) when the header is missing/blank/wrong; logs an error and rejects when the key or header name is unconfigured (an unset secret never authorizes anything).

It deliberately does not own the verify request/response DTOs or the credential-validation logic — those differ per method and stay in each service.

Usage

// Program.cs / startup
builder.Services.AddExternalDirectGrant(
    "otp", builder.Configuration, sectionName: "Otp", headerName: "X-Otp-Internal-Key");

// in the verify endpoint (FastEndpoints / minimal API)
public VerifyOtp(IMediator mediator, IExternalDirectGrantGate gate) { … }

if (!gate.IsAuthorized(HttpContext, "otp"))
{
    await Send.UnauthorizedAsync(ct);   // 401, no body — never leaks which check failed
    return;
}

Design notes

  • Constant-time: CryptographicOperations.FixedTimeEquals over UTF-8 bytes — no early-exit timing side channel on the secret.
  • Fail-closed: unconfigured key/header rejects everything (logged as an operator error, not a client 401 reason).
  • No body on reject: the caller returns 401 with no payload, so the gate never reveals missing-vs-wrong-key.
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

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.0.1 301 7/8/2026