Authagonal.Bff 0.25.2

dotnet add package Authagonal.Bff --version 0.25.2
                    
NuGet\Install-Package Authagonal.Bff -Version 0.25.2
                    
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="Authagonal.Bff" Version="0.25.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Authagonal.Bff" Version="0.25.2" />
                    
Directory.Packages.props
<PackageReference Include="Authagonal.Bff" />
                    
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 Authagonal.Bff --version 0.25.2
                    
#r "nuget: Authagonal.Bff, 0.25.2"
                    
#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 Authagonal.Bff@0.25.2
                    
#: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=Authagonal.Bff&version=0.25.2
                    
Install as a Cake Addin
#tool nuget:?package=Authagonal.Bff&version=0.25.2
                    
Install as a Cake Tool

Authagonal.Bff

Backend-for-Frontend (BFF) for SPAs that authenticate with Authagonal.

Your React/Vue/Angular app should never hold access or refresh tokens: anything in JS-reachable storage is exposed to XSS. This package is a confidential OIDC client you host on your own backend. It runs the authorization-code + PKCE flow server-side, keeps the tokens in a server-side session, and gives the browser nothing but an httpOnly session cookie. This is the pattern the IETF OAuth 2.0 for Browser-Based Apps BCP recommends.

Install

dotnet add package Authagonal.Bff

Wire it up

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthagonalBff(o =>
{
    o.Authority    = "https://acme-admin.authagonal.io"; // your tenant auth host
    o.ClientId     = builder.Configuration["Bff:ClientId"]!;
    o.ClientSecret = builder.Configuration["Bff:ClientSecret"]!;
    o.Scope        = ["openid", "profile", "email", "offline_access"]; // offline_access enables refresh
    o.PostLogoutRedirectUri = "https://app.acme.com/";
});

var app = builder.Build();

app.UseForwardedHeaders();  // required if you run behind a reverse proxy / ingress
app.MapAuthagonalBff();
app.MapFallbackToFile("index.html"); // your SPA

app.Run();

Register a BFF client in the Authagonal portal (confidential + PKCE + offline_access) with:

  • redirect URI https://app.acme.com/bff/callback
  • post-logout redirect URI https://app.acme.com/

Endpoints (mounted under /bff by default)

Route Purpose
GET /bff/login?returnUrl=/ Start login; redirects to Authagonal.
GET /bff/callback OIDC redirect URI (handled for you).
GET /bff/user { isAuthenticated, claims, sessionExpiresAt }. Requires the anti-forgery header.
GET\|POST /bff/logout Ends the session locally + at Authagonal.

From the browser

Every non-navigation call must carry a static anti-forgery header (defends against CSRF alongside SameSite=Lax):

const me = await fetch("/bff/user", { headers: { "X-Authagonal-Bff": "1" } }).then(r => r.json());
if (!me.isAuthenticated) window.location.href = "/bff/login?returnUrl=" + encodeURIComponent(location.pathname);

To log in / out, navigate (don't fetch): location.href = "/bff/login" / "/bff/logout".

Sessions & scaling

Sessions are stored via IDistributedCache. In-memory is the default; register a real distributed cache (e.g. Redis) before AddAuthagonalBff when you run more than one instance:

builder.Services.AddStackExchangeRedisCache(o => o.Configuration = "...");

A shared cache is not sufficient on its own — you also need a cross-replica refresh lock. The refresh single-flight is otherwise process-local, while the session and its rotating refresh token live in the cache every replica shares. Two replicas can read the same session, both see it needs refreshing, and both redeem the same refresh token; that is indistinguishable from a stolen-token replay, and the IdP's answer to replay is to revoke the whole grant family — so a multi-instance BFF can sign a user out everywhere as a matter of routine.

Supply the lock by either route. Register an ILeaseProvider — any backend works, because all the coordinator needs is "at most one holder for a short time", and the Azure, AWS and SQL providers each ship one:

builder.Services.AddAuthagonalClustering(/* … */);   // supplies ILeaseProvider

Or implement IBffRefreshLockStore on the session store, which is the shorter route when you have Redis and no clustering. It is a conditional write with a TTL, and it puts the lock in the backend the sessions already live in:

sealed class RedisBffSessionStore : IBffSessionStore, IBffRefreshLockStore
{
    // …the IBffSessionStore members…

    public async Task<bool> TryAcquireRefreshLockAsync(string sessionId, TimeSpan ttl, CancellationToken ct = default) =>
        await _db.StringSetAsync($"agbff:lock:{sessionId}", "1", ttl, When.NotExists);

    public Task ReleaseRefreshLockAsync(string sessionId, CancellationToken ct = default) =>
        _db.KeyDeleteAsync($"agbff:lock:{sessionId}");
}

The default store cannot offer this itself: IDistributedCache has no set-if-absent, so there is no conditional write to build a lock on. That is why this is a seam rather than something the library just does. The TypeScript package's own session store carries the same two methods.

With neither supplied, a multi-instance deployment depends on the IdP's refresh-reuse grace window (Auth:RefreshTokenReuseGraceSeconds) to absorb the double redemption — and in Authagonal's own server host that defaults to 0, strict. A BFF whose session store looks shared and has no lock now says so in a warning at startup, so this is not something you find out from a support ticket.

Extension points (the hosted seam)

Swap any of these to move the BFF onto other infrastructure:

  • IBffSessionStore — where sessions live (default: IDistributedCache).
  • ICookieProtector — cookie payload encryption (default: ASP.NET Data Protection).
  • ITokenClient — talking to Authagonal's token/revocation endpoints.

See docs/bff.md in the authagonal-cloud repo for the full protocol contract.

Product Compatible and additional computed target framework versions.
.NET 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 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.

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
0.25.2 43 8/12/2026
0.24.0 88 8/7/2026
0.23.0 84 8/5/2026
0.22.1 99 8/4/2026
0.22.0 100 8/3/2026
0.21.0 99 7/30/2026
0.20.0 90 7/27/2026
0.19.0 92 7/27/2026
0.18.0 90 7/27/2026
0.17.5 100 7/27/2026
0.17.4 96 7/27/2026
0.17.3 210 7/27/2026
0.17.2 94 7/27/2026
0.17.1 95 7/26/2026
0.17.0 93 7/26/2026
0.16.5 88 7/26/2026
0.16.4 99 7/26/2026
0.16.3 96 7/26/2026
0.16.2 95 7/25/2026
0.16.1 97 7/25/2026
Loading failed