Authagonal.Bff
0.23.0
dotnet add package Authagonal.Bff --version 0.23.0
NuGet\Install-Package Authagonal.Bff -Version 0.23.0
<PackageReference Include="Authagonal.Bff" Version="0.23.0" />
<PackageVersion Include="Authagonal.Bff" Version="0.23.0" />
<PackageReference Include="Authagonal.Bff" />
paket add Authagonal.Bff --version 0.23.0
#r "nuget: Authagonal.Bff, 0.23.0"
#:package Authagonal.Bff@0.23.0
#addin nuget:?package=Authagonal.Bff&version=0.23.0
#tool nuget:?package=Authagonal.Bff&version=0.23.0
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 — register an ILeaseProvider too. 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. BffRefreshCoordinator documents this in its own remarks and the
requirement was missing here.
Any backend works, because all the coordinator needs is "at most one holder": the Azure, AWS and SQL
providers each ship one through AddAuthagonalClustering.
builder.Services.AddAuthagonalClustering(/* … */); // supplies ILeaseProvider
With no lease provider registered, 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.
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 | Versions 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. |
-
net10.0
- Authagonal.Core (>= 0.23.0)
- Microsoft.IdentityModel.JsonWebTokens (>= 8.19.2)
- Microsoft.IdentityModel.Protocols.OpenIdConnect (>= 8.19.2)
-
net9.0
- Authagonal.Core (>= 0.23.0)
- Microsoft.IdentityModel.JsonWebTokens (>= 8.19.2)
- Microsoft.IdentityModel.Protocols.OpenIdConnect (>= 8.19.2)
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.23.0 | 0 | 8/5/2026 |
| 0.22.1 | 57 | 8/4/2026 |
| 0.22.0 | 69 | 8/3/2026 |
| 0.21.0 | 66 | 7/30/2026 |
| 0.20.0 | 84 | 7/27/2026 |
| 0.19.0 | 85 | 7/27/2026 |
| 0.18.0 | 84 | 7/27/2026 |
| 0.17.5 | 92 | 7/27/2026 |
| 0.17.4 | 86 | 7/27/2026 |
| 0.17.3 | 203 | 7/27/2026 |
| 0.17.2 | 89 | 7/27/2026 |
| 0.17.1 | 88 | 7/26/2026 |
| 0.17.0 | 85 | 7/26/2026 |
| 0.16.5 | 81 | 7/26/2026 |
| 0.16.4 | 94 | 7/26/2026 |
| 0.16.3 | 90 | 7/26/2026 |
| 0.16.2 | 90 | 7/25/2026 |
| 0.16.1 | 92 | 7/25/2026 |
| 0.16.0 | 99 | 7/25/2026 |
| 0.15.3 | 87 | 7/25/2026 |