Repository navigation
Authorization
By default the Folio UI is open to anyone who can reach the route. There are two ways to gate it, depending on whether your app already has authentication.
Scope note: both approaches below only gate the Folio UI. Neither protects the underlying OpenAPI document (
SpecUrl, typically served byMapOpenApi()or Swashbuckle) or your actual API endpoints — anyone who requests that JSON directly still gets the full endpoint list and schemas. Protect those the same way you'd protect any other endpoint (e.g..RequireAuthorization()on the spec endpoint).
If your app already has authentication (cookie, JWT bearer, ASP.NET Core Identity, an external provider, ...), point FolioOptions.AuthorizationPolicy at a policy name. Folio checks it via the app's own IAuthorizationService and challenges/forbids exactly like a normal [Authorize] endpoint would — it doesn't add a second auth system, it just asks yours.
builder.Services.AddAuthorization(options =>
options.AddPolicy("FolioAccess", policy => policy.RequireAuthenticatedUser()));
var app = builder.Build();
// Authentication/authorization must run before UseFolio so
// HttpContext.User is populated when it checks AuthorizationPolicy.
app.UseAuthentication();
app.UseAuthorization();
app.UseFolio(options =>
{
options.SpecUrl = "/openapi/v1.json";
options.AuthorizationPolicy = "FolioAccess";
options.LogoutUrl = "/account/logout"; // wherever your app signs out
});An unauthenticated request gets challenged (e.g. redirected to your login page for cookie auth, or a 401 for JWT bearer); an authenticated-but-unauthorized request gets a 403.
If you have no auth system yet and just want a login in front of the docs, AddFolioAuth()/UseFolioAuth() handles everything: cookie issuance, a login page styled to match Folio's own dark theme, and the login/logout routes — then wires itself into UseFolio(...) automatically (no need to set AuthorizationPolicy/LogoutUrl yourself).
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddFolioAuth(options =>
{
options.Users = builder.Configuration.GetSection("Users").Get<List<FolioUser>>() ?? [];
});
var app = builder.Build();
app.UseFolioAuth();
app.UseFolio(options => options.SpecUrl = "/openapi/v1.json");
app.Run();// appsettings.json
{
"Users": [
{ "Username": "admin", "Password": "correct-horse-battery-staple" }
]
}| Property | Default | Description |
|---|---|---|
Users |
[] |
Accounts accepted by the login page. Empty by default — set this or nobody can sign in. |
LoginPath |
"/folio/login" |
Path the login page is served at. Independent of RoutePrefix — if you change that, set this to match. |
LogoutPath |
"/folio/logout" |
Path that signs the user out and redirects back to LoginPath. |
CookieName |
"Folio.Auth" |
Name of the authentication cookie. |
Title |
null |
Brand text on the login page. Defaults to "Folio" — a separate setting from FolioOptions.Title. |
- Credentials are submitted via
POST, not the query string. -
Usersis a plain in-memory list, compared with a constant-time check — fine for gating an internal docs UI behind a shared login, not a substitute for a real identity system. Passwords are stored as plain text in configuration; don't reuse a password that matters elsewhere. - The cookie scheme registered by
AddFolioAuth()is never made your app's default authentication scheme, so it can't interfere with any other auth you add later — Folio always authenticates against its own scheme by name. -
ReturnUrl(used to send you back to the page you were trying to reach) is validated as a same-app relative path, rejecting absolute and protocol-relative (//host/...) values.
Calling UseFolioAuth() before UseFolio(...) is the clearer read, but the order doesn't actually matter — UseFolio's gate always lets the login/logout paths through to whichever middleware owns them, so getting the order backwards won't cause a redirect loop.