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 .srh file 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.

csharp
// 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 = true disables the binding entirely. It exists for controlled scenarios (e.g. server-to-server rendering); leave it false in 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:

  1. a grant exists for the token,
  2. it has not expired (grant lifetime = the document's TimeOut),
  3. the requesting ASP.NET session ID matches the one that opened the document,
  4. if the opener was authenticated, the requesting user's NameIdentifier claim 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.

csharp
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 call app.UseSession() before the Doconut middleware branch.
  • Make sure your session cookie policy lets the widget's requests carry the cookie (SameSite, HTTPS).
  • Use CloseDocument when 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?