Sessions & Security
Document sessions and access control
A Doconut token identifies an open document. If any request carrying it were served, whoever obtained it could read every page. This page explains what a document session holds, how long it lives, and how DocImage.axd decides who may see its pages — on one server and in a web farm.
What a document session holds
Each successful OpenDocumentAsync creates one session, kept in memory:
- the loaded format viewer — the engine instance holding the parsed document,
- per-page state — rotation, flips, and the annotation data the user applies in the widget,
- the optional search index, built on the first search (or loaded from a pre-built
.srhfile), - 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 ends — by expiration or by CloseDocument(token) — its document engine is released and its memory freed 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 page must open the document again to get a new token. On a single server, recycling the application pool ends every session too.
Pages are served only to the session that opened them
With UnsafeMode = false — the default — opening a document grants access to the ASP.NET session of the request that opened it, by writing a secure-{token} entry into that session. DocImage.axd requires session state itself, and serves a page only when the requesting session holds that entry. Any other browser presenting the same token gets an error image reading You Are Not Authorized To View This Page.
Doconut.DoconutHost.Initialize(options =>
{
// false (the default): DocImage.axd serves a page only to the ASP.NET session that opened
// the document. The handler asks for session state itself; it only needs the site not to
// turn session state off in Web.config.
options.UnsafeMode = false;
});Three practical consequences:
- The open endpoint needs session state. MVC controllers and Web Forms pages have it by default. A custom
IHttpHandlerdoes not unless it implementsSystem.Web.SessionState.IRequiresSessionState. An open that runs without a session has nothing to grant, so the viewer shows "You Are Not Authorized To View This Page." on every page. - Session state must stay on.
<sessionState mode="Off" />inWeb.configdisables both the grant and the check, and every page is refused. - The browser must send the session cookie. The widget's requests are same-origin, so they carry
ASP.NET_SessionIdautomatically. A client without a cookie jar — a script, a server-to-server call, a cross-origin setup that strips cookies — is refused. That is the feature working.
UnsafeMode = true switches the check off: any request carrying the token is served. It is one global switch for every document, with no per-document override. Leave it false.
Revocation
CloseDocument(token) does more than free memory: it removes the secure-{token} entry from the current request's session and revokes the token, so a closed token is refused at once.
Web farms: signed tickets
In a farm, the next request can land on a server that never saw the document, in a different worker process, with its own ASP.NET session. The session grant cannot follow it. Distributed mode replaces it with a signed ticket bound to the browser: every node shares one store for document sessions and one signing key, and DocImage.axd accepts a request when its ticket is valid for that token, that browser, and — when the user is signed in — that user.
The setup, identical on every node:
// Global.asax.cs - Application_Start
Doconut.DoconutHost.Initialize(
options => options.UnsafeMode = false, // the ticket check only runs when this is false
services => services.AddDoconutDistributed(d =>
{
d.Store = CloudLocation.FileShare;
d.FileShareRootPath = @"\\fileserver\doconut-shared"; // a folder every node can reach
d.SigningKey = farmKey; // at least 32 bytes, the SAME on every node
}));The open endpoint then binds the ticket to the browser before opening, and returns the ticket with the token:
- set
DocOptions.BrowserId = DoconutFarmBinding.EnsureBrowserId(System.Web.HttpContext.Current, securityOptions)— it issues thedoconut-clientcookie the ticket is tied to — andDocOptions.UserId = DoconutFarmBinding.ResolveUserId(System.Web.HttpContext.Current); - after
OpenDocumentAsync, returnviewer.AccessTokenalongside the token; - in the page, pass both to the widget:
objViewer.View(token, access).
Doconut.MvcSample.WebFarm in the samples package is a complete, runnable farm node, and the migration guide beside it explains each step.
Keep the signing key out of source control: it both signs the tickets and encrypts the documents in the shared store. A node with a different key rejects every ticket the others issued.
Test a farm with a real browser, or a script that keeps cookies. A client without the doconut-client cookie is not answered with 401: it receives the "not authorized" error image with status 200, about 2 KB, where a real page is tens to hundreds of kilobytes.
Production checklist
- Keep
UnsafeMode = false(the default). - Open documents from requests that have session state, and never turn session state off.
- Make sure the session cookie reaches
DocImage.axd(same origin, HTTPS, aSameSitepolicy that allows it). - In a farm, use distributed mode with one signing key shared by every node.
- Call
CloseDocumentwhen the user leaves a document — memory and security both benefit. - Never log or share tokens; treat them as short-lived credentials.
Byla tato stránka užitečná?