Upload de pièces jointes depuis un widget personnalisé bloqué par CORS

Bonjour,

Je travaille sur un widget personnalisé pour le suivi d’élèves à besoins éducatifs particuliers (EBEP) dans les écoles. Le widget est hébergé sur La Forge des communs numériques et intégré dans Grist (grist.numerique.gouv.fr) via la fonctionnalité de widget personnalisé.

Je voudrais permettre à l’utilisateur d’ajouter des pièces jointes (compte-rendus, documents de suivi…) directement depuis l’interface du widget, sans avoir à passer par la vue table.

J’utilise grist.docApi.getAccessToken({ readOnly: false }) pour récupérer un token et le baseUrl de l’API REST du document, puis je tente un fetch() en POST :

const { token, baseUrl } = await grist.docApi.getAccessToken({ readOnly: false }); const formData = new FormData(); formData.append("upload", file, file.name); const resp = await fetch(${baseUrl}/attachments?auth=${encodeURIComponent(token)}, { method: "POST", body: formData, });

  • La lecture des métadonnées des pièces jointes existantes (GET /attachments/:id) fonctionne.
  • Le téléchargement via window.open(url, "_blank", "noopener,noreferrer") fonctionne (pas de CORS car c’est une navigation, pas un fetch).

Le [fetch()] en POST est bloqué par le navigateur (erreur CORS). L’origine du widget (domaine de La Forge) n’est pas autorisée à faire des requêtes cross-origin vers le serveur Grist, même avec un token valide dans les paramètres de l’URL et des permissions Full document access accordées au widget.

Extraits des messages d’erreur de la console :
"POST https://grist.numerique.gouv.fr/o/docs/api/docs//attachments?auth=

Raison : l’en-tête CORS « Access-Control-Allow-Origin » est manquant.
Code d’état : 401.

TypeError: NetworkError when attempting to fetch resource."

  • Y a-t-il une méthode dans l’API plugin Grist côté widget (grist.docApi.*) qui permette d’uploader un fichier sans passer par un fetch cross-origin (c’est-à-dire en passant par le canal postMessage que Grist utilise déjà pour les autres appels API) ?
  • Est-ce que la configuration CORS de l’instance http://grist.numerique.gouv.fr peut être ajustée pour autoriser les origines déclarées dans les widgets personnalisés ?
  • Existe-t-il un contournement pour ce cas d’usage ?

Merci d’avance pour vos pistes !

Bonjour c’est déjà possible avec le code suivant Import et sauvegarde d'un fichier via un Custom Widget Builder / upload - charger PJ depuis custom widget - #8 par audezu

Cependant il y a un souci a priori obscur et complexe provoquant la ré-apparition de cette erreur CORS… @ohemelaar est-ce que tu pourras regarder stp, je viens de tester et le souci est revenu sur l’instance DINUM (ANCT ok)

doc test DINUM : https://grist.numerique.gouv.fr/o/docs/doc/6RmTTpquReku
doc test ANCT : Ex - afficher PJ / charger PJ / télécharger PJ - Grist

Merci beaucoup ! J’avais cherché dans le forum, mais pas assez bien :sweat_smile:

J’ai testé, mais ça ne semble effectivement pas fonctionner actuellement. Voici le diagnostic fait par Claude (Sonnet 4.6) d’après les logs de la console :

L’en-tête X-Requested-With: XMLHttpRequest est un en-tête non standard, ce qui déclenche automatiquement une requête de pré-vérification CORS (OPTIONS) avant le vrai POST. Sur l’instance ANCT, cette requête OPTIONS est bien traitée par Grist. Sur l’instance DINUM, elle est bloquée avant d’atteindre Grist par un proxy ou WAF intermédiaire, avec un code 403 — d’où l’échec.

OPTIONS .../attachments?auth=<token> → CORS Preflight Did Not Succeed — Code d'état : 403

Le problème n’est donc pas dans Grist lui-même, mais dans la configuration réseau de l’instance grist.numerique.gouv.fr. Il faudrait que l’infrastructure autorise les requêtes OPTIONS cross-origin provenant des origines déclarées dans les widgets personnalisés (ou que les en-têtes CORS correspondants soient renvoyés par le proxy).