Sessions & Security
Document sessions and access control
A Doconut token is powerful: whoever presents it could request every page of the document if it were not bound to the opening session. This page explains what a session holds, how long it lives, and the checks UseDoconut() enables by default.
What a document session holds
Each successful OpenDocumentAsync creates one session in IMemoryCache:
- the loaded format viewer (the document engine instance holding the parsed document),
- per-page state — rotation, flips, and annotation data the user applies in the widget,
- the optional search index, built lazily on the first search (or loaded from a pre-built
.srhfile in web-farm scenarios), - the session watermark from
DocOptions.Watermark.
Lifetime
Sessions expire on a sliding window: DocOptions.TimeOut minutes (default 60), reset by every request that presents the token. When a session is evicted — by expiration or by CloseDocument(token) — its eviction callback disposes the document engine and frees the associated memory immediately.
// A short-lived session for a one-shot preview
var token = await viewer.OpenDocumentAsync(path, new DocOptions { TimeOut = 10 });A request with an expired token gets an error image reading Document session not found. Please re-open document. — the client must re-open to obtain a fresh token.
Built-in token binding
With UnsafeMode = false (the default), OpenDocumentAsync binds the new token to the ASP.NET session of the HTTP request that opened it, by writing a secure-{token} marker into that session. The Doconut middleware then refuses to serve pages to any other browser session:
- Different browser/session presenting a stolen token → error image
You Are Not Authorized To View This Page. - Session middleware not registered → HTTP 500
Session middleware not configured. Call UseSession() before UseDoconut().
This is why the Quick Start insists on AddSession() + app.UseSession() before the Doconut branch. Two practical consequences:
- The client must send the ASP.NET session cookie with page requests. Cross-origin setups that strip cookies (or an API client with no cookie jar) will fail the check — that is the feature working, not a bug.
options.UnsafeMode = truedisables the binding entirely. It exists for controlled scenarios (e.g. server-to-server rendering); leave itfalsein production.
Token binding is controlled solely by this global UnsafeMode switch — it is on by default (UnsafeMode = false) and applies to every session. There is no per-document opt-out; setting UnsafeMode = true disables the binding globally.
Access grants and authenticated users
When UnsafeMode is false, UseDoconut() inserts DocumentAccessMiddleware automatically before the page middleware. Do not register it a second time. When a request carries a token, it looks up the access grant recorded when the document was opened and authorizes only if all of these hold:
- a grant exists for the token,
- it has not expired (grant lifetime = the document's
TimeOut), - the requesting ASP.NET session ID matches the one that opened the document,
- if the opener was authenticated, the requesting user's
NameIdentifierclaim matches too.
Failures return 403 — as a PNG error image for page/thumbnail requests, as plain text otherwise. The message and token query key come from DocumentSecurityOptions (TokenQueryKey, default "token"; UnauthorizedMessage, default "You Are Not Authorized To View This Page."). Configure those options through ASP.NET Core DI before building the app. If session state is unavailable the middleware fails closed with HTTP 500: ASP.NET Session is required for Doconut document security.
builder.Services.Configure<Doconut.Security.DocumentSecurityOptions>(options =>
{
options.TokenQueryKey = "token";
options.UnauthorizedMessage = "You Are Not Authorized To View This Page.";
});The core page middleware then verifies the secure-{token} session marker before it serves the document. With UnsafeMode = true, UseDoconut() skips the access middleware and the core marker check is also disabled.
Revocation
CloseDocument(token) doesn't just free memory — it also removes the secure-{token} marker and revokes the access grant, so a closed token is dead on both security layers immediately.
Checklist for production
- Keep
UnsafeMode = false(the default) — this global switch is what binds tokens to sessions. - Register
AddSession()and callapp.UseSession()before the Doconut middleware branch. - Make sure your session cookie policy lets the widget's requests carry the cookie (
SameSite, HTTPS). - Use
CloseDocumentwhen the user leaves the document — memory and security both benefit. - Never log or share tokens; treat them as short-lived credentials.
Was this page helpful?