Sessions et Sécurité

Sessions de documents et contrôle d'accès

Un jeton Doconut est puissant : quiconque le présente pourrait demander chaque page du document s'il n'était pas lié à la session d'ouverture. Cette page explique ce qu'une session contient, combien de temps elle vit et les vérifications que UseDoconut() active par défaut.

Ce que contient une session de document

Chaque OpenDocumentAsync réussi crée une session dans IMemoryCache :

  • le visualiseur de format chargé (l'instance du moteur de document contenant le document analysé),
  • état par page — rotation, retournements et données d'annotation que l'utilisateur applique dans le widget,
  • l'index de recherche optionnel, construit paresseusement lors de la première recherche (ou chargé à partir d'un fichier .srh pré‑construit lorsque les pages sont servies depuis un stockage partagé),
  • le filigrane de la session provenant de DocOptions.Watermark.

Durée de vie

Les sessions expirent selon une fenêtre glissante : DocOptions.TimeOut minutes (par défaut 60), réinitialisée à chaque requête présentant le jeton. Lorsqu'une session est évincée — par expiration ou par CloseDocument(token) — son rappel d'éviction libère le moteur de document et libère immédiatement la mémoire associée.

csharp
// Une session de courte durée pour un aperçu unique
var token = await viewer.OpenDocumentAsync(path, new DocOptions { TimeOut = 10 });

Une requête avec un jeton expiré reçoit une image d'erreur affichant Document session not found. Please re-open document. — le client doit rouvrir le document pour obtenir un nouveau jeton.

Liaison de jeton intégrée

Avec UnsafeMode = false (la valeur par défaut), OpenDocumentAsync lie le nouveau jeton à la session ASP.NET de la requête HTTP qui l'a ouverte, en écrivant un marqueur secure-{token} dans cette session. Le middleware Doconut refuse alors de servir les pages à toute autre session de navigateur :

  • Navigateur/session différent présentant un jeton volé → image d'erreur You Are Not Authorized To View This Page.
  • Middleware de session non enregistré → HTTP 500 Session middleware not configured. Call UseSession() before UseDoconut().

Voici pourquoi le Quick Start insiste sur AddSession() + app.UseSession() avant la branche Doconut. Deux conséquences pratiques :

  • Le client doit envoyer le cookie de session ASP.NET avec les requêtes de page. Les configurations cross‑origin qui suppriment les cookies (ou un client API sans jar de cookies) échoueront la vérification — c'est le fonctionnement prévu, pas un bug.
  • options.UnsafeMode = true désactive complètement la liaison. Elle existe pour des scénarios contrôlés ; laissez‑la à false en production sur un nœud unique.

Notez l'orthographe. La bibliothèque .NET Standard précédente appelait cette propriété UnSafeMode et elle était transmise au middleware ; ici elle s'appelle UnsafeMode et elle est définie une fois dans AddDoconut().

La liaison de jeton est contrôlée uniquement par cet interrupteur global UnsafeMode — il est activé par défaut (UnsafeMode = false) et s'applique à chaque session. Il n'existe aucune option de désactivation par document ; définir UnsafeMode = true désactive la liaison globalement.

Autorisations d'accès et utilisateurs authentifiés

Lorsque UnsafeMode est false, UseDoconut() insère automatiquement le middleware d'accès aux documents avant le middleware de page. Ne l'enregistrez pas une seconde fois. Lorsqu'une requête porte un jeton, elle recherche l'autorisation d'accès enregistrée lors de l'ouverture du document et autorise uniquement si toutes les conditions suivantes sont remplies :

  1. une autorisation existe pour le jeton,
  2. elle n'est pas expirée (durée de vie de l'autorisation = le TimeOut du document),
  3. l'ID de session ASP.NET de la requête correspond à celui qui a ouvert le document,
  4. si l'ouvreur était authentifié, la revendication NameIdentifier de l'utilisateur demandeur correspond également.

Les échecs renvoient 403 — sous forme d'image PNG d'erreur pour les requêtes de page/vignette, sinon en texte brut. Le message et la clé de requête du jeton proviennent de DocumentSecurityOptions (TokenQueryKey, par défaut "token" ; UnauthorizedMessage, par défaut "You Are Not Authorized To View This Page."). Configurez ces options via l'injection de dépendances ASP.NET Core avant de construire l'application. Si l'état de session n'est pas disponible, le middleware échoue en mode fermé avec 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.";
});

Le middleware de page principal vérifie alors le marqueur de session secure-{token} avant de servir le document. Avec UnsafeMode = true, UseDoconut() saute le middleware d'accès et la vérification du marqueur principal est également désactivée.

Sécurité sur plusieurs nœuds

Les jetons liés à une session supposent que le nœud servant une page a également vu la requête qui a ouvert le document. Cette hypothèse ne tient pas lorsque plusieurs nœuds servent les mêmes documents depuis un stockage partagé : une requête de page peut atterrir sur un nœud qui n'a jamais vu l'ouverture, et la vérification par session la rejette.

La solution n'est pas de laisser les documents non protégés. Utilisez des tickets d'accès signés via AddDoconutDistributedDocumentSecurity(...), qui remplacent la vérification de session par un credential que chaque nœud peut vérifier indépendamment. L'ensemble complet est décrit dans Déploiements distribués.

Révocation

CloseDocument(token) ne libère pas seulement la mémoire — il supprime également le marqueur secure-{token} et révoque l'autorisation d'accès, de sorte qu'un jeton fermé est immédiatement inactif sur les deux couches de sécurité.

Checklist pour la production

  • Conservez UnsafeMode = false (la valeur par défaut) sur un nœud unique — cet interrupteur global lie les jetons aux sessions.
  • Enregistrez AddDistributedMemoryCache() (les applications MVC et Razor Pages l'ont déjà) et AddSession(), puis appelez app.UseSession() avant la branche du middleware Doconut.
  • Assurez‑vous que la politique de cookie de session permet aux requêtes du widget de transmettre le cookie (SameSite, HTTPS).
  • Utilisez CloseDocument lorsque l'utilisateur quitte le document — la mémoire et la sécurité en bénéficient.
  • Ne jamais consigner ou partager les jetons ; traitez‑les comme des credentials à courte durée de vie.
  • Vous servez depuis un stockage partagé ? Passez aux tickets d'accès signés plutôt que d'accepter des documents non protégés.

Cette page était-elle utile ?