Bewit.Extensions.HotChocolate 8.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Bewit.Extensions.HotChocolate --version 8.0.0
                    
NuGet\Install-Package Bewit.Extensions.HotChocolate -Version 8.0.0
                    
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="Bewit.Extensions.HotChocolate" Version="8.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Bewit.Extensions.HotChocolate" Version="8.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Bewit.Extensions.HotChocolate" />
                    
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 Bewit.Extensions.HotChocolate --version 8.0.0
                    
#r "nuget: Bewit.Extensions.HotChocolate, 8.0.0"
                    
#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 Bewit.Extensions.HotChocolate@8.0.0
                    
#: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=Bewit.Extensions.HotChocolate&version=8.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Bewit.Extensions.HotChocolate&version=8.0.0
                    
Install as a Cake Tool

Bewit

Bewit is an authentication scheme for secure, temporary access tokens.

Bewit enables authentication in use cases where cookies and auth headers can't be used — file downloads, temporary links, single-use tokens, and share links with admin-controlled expiry.

Features

  • Self-Contained tokens — expiry embedded in the token (stateless)
  • Server-Controlled tokens — expiry managed in the database; requires UseMongoDb() or UseNonceRepository()
  • Token revocation — type-safe IBewitTokenRevoker<T> to revoke tokens by identifier
  • Multi-tenancy — different secrets and modes per payload type
  • MongoDB persistence — with OIDC auth support (Azure.Identity)
  • HotChocolate integration[Bewit<T>] attribute for GraphQL resolvers
  • MVC integration[BewitMvc], [FromBewit], [BewitUrlAuthorization] filters
  • Aspire-ready — connection string pattern for distributed apps

Quick Start

Install

dotnet add package Bewit
dotnet add package Bewit.Generation
dotnet add package Bewit.Validation

Registration

From appsettings.json:

{
  "Bewit": {
    "Secret": "your-secret-at-least-32-chars!",
    "TokenDuration": "00:05:00",
    "ExpiryMode": "SelfContained"
  }
}
services.AddBewit(bewit =>
{
    bewit.BindConfiguration("Bewit");
    bewit.AddPayload<string>();
});

services.AddBewitGeneration<string>();
services.AddBewitValidation<string>();

Or code-only:

services.AddBewit(bewit =>
{
    bewit.ConfigureOptions(o =>
    {
        o.Secret = "your-secret-at-least-32-chars!";
        o.TokenDuration = TimeSpan.FromMinutes(5);
        o.ExpiryMode = ExpiryMode.SelfContained;
    });

    bewit.AddPayload<string>();
});

services.AddBewitGeneration<string>();
services.AddBewitValidation<string>();

Both can be combined — BindConfiguration loads from appsettings first, then ConfigureOptions overrides specific values.

Configuration

v7.0 supports appsettings.json binding, code-based configuration, or both. When combined, code wins (standard .NET options layering: Bind → Configure → PostConfigure).

From appsettings.json

{
  "Bewit": {
    "Secret": "your-secret-at-least-32-chars!",
    "TokenDuration": "00:05:00",
    "ExpiryMode": "SelfContained"
  }
}
services.AddBewit(bewit =>
{
    bewit.BindConfiguration("Bewit");
    bewit.AddPayload<string>();
});

Code overrides on top of config

Aspire-friendly — IConfiguration is read at resolve time, not registration time:

services.AddBewit(bewit =>
{
    bewit.BindConfiguration("Bewit");
    bewit.ConfigureOptions(o =>
    {
        o.TokenDuration = TimeSpan.FromMinutes(30); // overrides appsettings value
    });
    bewit.AddPayload<string>();
});

Per-payload config section

Each payload type can bind to its own config section:

{
  "Bewit": {
    "Secret": "global-secret",
    "TokenDuration": "00:05:00"
  },
  "Bewit:Downloads": {
    "Secret": "download-secret",
    "TokenDuration": "00:01:00",
    "ExpiryMode": "ServerControlled"
  }
}
services.AddBewit(bewit =>
{
    bewit.BindConfiguration("Bewit");
    bewit.AddPayload<string>();
    bewit.AddPayload<DownloadPayload>(p =>
        p.BindConfiguration("Bewit:Downloads")); // overrides global section
});

Code-only (no appsettings)

services.AddBewit(bewit =>
{
    bewit.ConfigureOptions(o =>
    {
        o.Secret = "your-secret";
        o.TokenDuration = TimeSpan.FromMinutes(5);
    });
    bewit.AddPayload<string>();
});

Generate a Token

var generator = serviceProvider.GetRequiredService<IBewitTokenGenerator<string>>();

BewitToken<string> token = await generator.GenerateBewitTokenAsync(
    "my-payload", null, cancellationToken);

Validate a Token

var validator = serviceProvider.GetRequiredService<IBewitTokenValidator<string>>();

string payload = await validator.ValidateBewitTokenAsync(token, cancellationToken);

Server-Controlled Tokens

ExpiryMode.ServerControlled requires a persistent nonce repository. The app fails at startup if ServerControlled is set without UseMongoDb() / UseNonceRepository().

services.AddBewit(bewit =>
{
    bewit.ConfigureOptions(o =>
    {
        o.Secret = "your-secret";
        o.TokenDuration = TimeSpan.FromDays(7);
        o.ExpiryMode = ExpiryMode.ServerControlled;
    });

    bewit.UseMongoDb(mongo =>
    {
        mongo.ConnectionString = "mongodb://localhost:27017";
        mongo.DatabaseName = "myapp";
    });

    bewit.AddPayload<ShareLinkPayload>();
});

Token Revocation

Inject IBewitTokenRevoker<T> to revoke all tokens for an identifier — no keyed DI magic strings needed:

public class Mutation(IBewitTokenRevoker<BarPayload> revoker)
{
    public async Task<string> InvalidateTokens(string identifier, CancellationToken ct)
    {
        await revoker.RevokeByIdentifierAsync(identifier, ct);

        return identifier;
    }
}

MongoDB with OIDC (Azure Cosmos DB)

bewit.UseMongoDb(mongo =>
{
    mongo.ConnectionString = "mongodb+srv://...";
    mongo.DatabaseName = "mydb";
    mongo.AuthType = MongoAuthType.Oidc;
    mongo.OidcScopes = ["https://cosmos-db-scope/.default"];
});

Or reuse an existing IMongoDatabase from DI — the recommended approach when the service already uses MongoDB.Extensions.Context:

// Register the context once
services.AddMongoDbContext<MyDbContext, IMyDbContext>(
    configurationSection: "MongoDb");

// Bewit reuses its IMongoDatabase — no second connection opened
services.AddBewit(bewit =>
{
    bewit.UseMongoDb(
        sp => sp.GetRequiredService<IMyDbContext>().Database,
        mongo => { mongo.NonceUsage = NonceUsage.OneTime; });

    bewit.AddPayload<MyPayload>(p =>
        p.ConfigureOptions(o => o.ExpiryMode = ExpiryMode.ServerControlled));
});

This shares the connection pool and inherits the convention packs, serializer registrations, and read/write concerns configured on the context.

Multiple Payloads with Shared MongoDB

All payloads inherit the builder-level MongoDB and options. Per-payload overrides are possible:

services.AddBewit(bewit =>
{
    bewit.ConfigureOptions(o =>
    {
        o.Secret = "your-secret";
        o.ExpiryMode = ExpiryMode.ServerControlled;
    });

    bewit.UseMongoDb(mongo =>
    {
        mongo.ConnectionString = "mongodb://localhost:27017";
        mongo.DatabaseName = "myapp";
        mongo.NonceUsage = NonceUsage.ReUse;
    });

    bewit.AddPayload<NominationBewitContext>();
    bewit.AddPayload<UserRegistrationBewitContext>();
    bewit.AddPayload<DocumentDownloadBewitContext>();

    // Override: self-contained, no MongoDB needed
    bewit.AddPayload<DownloadBewitContext>(p =>
    {
        p.ConfigureOptions(o =>
        {
            o.ExpiryMode = ExpiryMode.SelfContained;
            o.TokenDuration = TimeSpan.FromMinutes(5);
        });
    });
});

HotChocolate Integration

dotnet add package Bewit.Extensions.HotChocolate

[Bewit<T>] Attribute

[Mutation]
[Bewit<NominationBewitContext>(ExceptionType = typeof(BewitValidationException))]
public static async Task<NominationDto> AssignNomineeAsync(
    [Service] IHttpContextAccessor httpContextAccessor, ...)
{
    var context = httpContextAccessor.GetBewitPayload<NominationBewitContext>();
}

Setup

app.UseBewitTokenExtraction();

HTTP Endpoint Integration

dotnet add package Bewit.Http

Apply authorization to individual routes or route groups:

app.MapGet("/files/{id}", (string id) => ...)
    .AddBewitAuthorization<MyPayload>();

// or protect a group of endpoints:
app.MapGroup("/api/files")
    .AddBewitAuthorization<MyPayload>();

The filter validates the token from the configured header, query parameter, or pre-extracted HttpContext.Items entry, and makes the payload available via GetBewitPayload<T>().

Middleware (global)

Protect all endpoints via middleware:

app.UseBewitEndpointAuthorization<MyPayload>();

Token Extraction

All extensions (HotChocolate, Http, Mvc) read the bewit token from the same configurable sources: an HTTP header and/or a query parameter. Header takes precedence when both are present.

Defaults match the v6.x behavior:

  • Header: bewitToken
  • Query parameter: bewit

Code configuration

services.AddBewit(bewit =>
{
    bewit.ConfigureTokenExtraction(o =>
    {
        o.HeaderName = "X-Custom-Token";
        o.QueryParamName = "token";
    });

    bewit.AddPayload<string>();
});

appsettings.json

{
  "Bewit:TokenExtraction": {
    "HeaderName": "X-Custom-Token",
    "QueryParamName": "token"
  }
}
services.AddBewit(bewit =>
{
    bewit.BindTokenExtractionConfiguration("Bewit:TokenExtraction");
    bewit.AddPayload<string>();
});

Both can be combined — BindTokenExtractionConfiguration loads from appsettings first, then ConfigureTokenExtraction overrides specific values (standard .NET options layering).

MVC Integration

dotnet add package Bewit.Extensions.Mvc
[BewitUrlAuthorization]
[HttpGet("download/{id}")]
public IActionResult Download(string id) { ... }

Packages

Package Description
Bewit Core abstractions, models, crypto, DI
Bewit.Generation Token generation
Bewit.Validation Token validation
Bewit.Storage.MongoDB MongoDB nonce repository with OIDC support
Bewit.Extensions.HotChocolate HotChocolate [Bewit<T>], middleware
Bewit.Extensions.Mvc MVC filters and parameter binding
Bewit.Http Minimal API endpoint authorization

Migration

See Migration Guide v7.

Community

This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community. For more information, see the Swiss Life OSS Code of Conduct.

Product 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
8.1.0 432 5/23/2026
8.0.0 161 5/12/2026
7.0.0 109 5/8/2026
7.0.0-preview.5 52 5/6/2026
7.0.0-preview.3 57 5/4/2026
7.0.0-preview.2 63 5/4/2026
7.0.0-preview.1 60 5/4/2026
6.1.0-preview.1 66 4/28/2026
6.0.0 1,239 12/3/2025
6.0.0-preview.3 654 12/3/2025
6.0.0-preview.2 232 4/23/2025
6.0.0-preview.1 2,784 4/23/2025
5.0.0 2,491 4/23/2025
5.0.0-rc.2 1,477 11/14/2024
5.0.0-rc.1 153 10/17/2024
5.0.0-preview.9 225 4/23/2025
5.0.0-preview.8 222 4/23/2025
5.0.0-preview.7 163 10/9/2024
5.0.0-preview.6 166 10/9/2024
5.0.0-preview.5 138 10/8/2024
Loading failed