
# URL signées

Les URL signées accordent un accès temporaire et restreint aux fichiers privés sans exposer les identifiants de stockage ni rendre le fichier publiquement permanent. Une URL signée intègre un jeton HMAC-SHA256 (signé avec `AUTH_SECRET`) qui encode le chemin, l'opération et l'expiration. Falsifier n'importe quelle partie invalide le jeton.

Les URL signées fonctionnent sur les trois backends de stockage. Pour les backends S3, Sovrium délègue à la présignature S3 native ; pour les [backends local et bytea](/fr/docs/buckets-overview#storage-backends), il génère et valide les signatures en interne — aucun SDK AWS requis à la couche application.

## Accès public vs privé

Par défaut, les fichiers sont privés et requièrent soit une authentification soit une URL signée. Les opérateurs peuvent découper des préfixes de chemin toujours publics.

| Variable                 | Par défaut | Objectif                                                                                 |
| ------------------------ | ---------- | ---------------------------------------------------------------------------------------- |
| `STORAGE_DEFAULT_ACCESS` | `private`  | Niveau d'accès par défaut pour les nouveaux fichiers (`private` ou `public`).            |
| `STORAGE_PUBLIC_PATHS`   | (aucun)    | Préfixes de chemin séparés par des virgules servis sans authentification ni URL signées. |

```bash
STORAGE_DEFAULT_ACCESS=private
STORAGE_PUBLIC_PATHS=assets/,avatars/,logos/
```

| Comportement                                              | Résultat                                        |
| --------------------------------------------------------- | ----------------------------------------------- |
| Fichier sous un préfixe `STORAGE_PUBLIC_PATHS`            | Servi sans authentification ni URL signée.      |
| Fichier hors des chemins publics, pas d'URL signée valide | `403 Forbidden`.                                |
| `STORAGE_DEFAULT_ACCESS=public`                           | Tous les fichiers accessibles sans URL signées. |

La configuration des chemins publics est validée au démarrage — pas de caractères génériques en fin, pas de préfixes qui se chevauchent. Un bucket marqué `public: true` dans le schéma est l'équivalent par bucket (voir [Présentation des buckets](/fr/docs/buckets-overview#public-vs-private)).

## URL signées de téléchargement

Demandez une URL de téléchargement limitée dans le temps pour un fichier privé.

```http
POST /api/buckets/{bucket}/sign
Content-Type: application/json
Authorization: Bearer <token>

{
  "path": "documents/contract.pdf",
  "expiresIn": 3600,
  "operation": "download"
}
```

```json
{
  "signedUrl": "https://app.example.com/api/buckets/documents/files/documents/contract.pdf?token=eyJhbGciOiJIUzI1NiJ9...",
  "path": "documents/contract.pdf",
  "expiresAt": "2026-04-05T11:00:00Z"
}
```

| Paramètre   | Type    | Requis | Par défaut | Description                                            |
| ----------- | ------- | ------ | ---------- | ------------------------------------------------------ |
| `path`      | string  | Oui    | —          | Chemin du fichier relatif à la racine de stockage.     |
| `expiresIn` | integer | Non    | `3600`     | Expiration en secondes. Plage : 60 à 604800 (7 jours). |
| `operation` | string  | Non    | `download` | `download` ou `upload`.                                |

| Comportement                   | Résultat                                            |
| ------------------------------ | --------------------------------------------------- |
| Chemin valide                  | URL signée plus horodatage `expiresAt`.             |
| Accès à l'URL avant expiration | Fichier servi sans authentification supplémentaire. |
| Accès après expiration         | `403 Forbidden`.                                    |
| `expiresIn` < 60 ou > 604800   | `400 Bad Request`.                                  |
| Chemin inexistant              | `404 Not Found`.                                    |
| Jeton falsifié                 | `403 Forbidden`.                                    |

## URL signées de téléversement

Émettez un jeton d'écriture pour qu'un navigateur puisse téléverser directement vers le stockage sans faire transiter les octets par le serveur d'application.

```http
POST /api/buckets/{bucket}/sign
Content-Type: application/json
Authorization: Bearer <token>

{
  "path": "uploads/user-123/profile-photo.jpg",
  "expiresIn": 600,
  "operation": "upload",
  "contentType": "image/jpeg",
  "maxSize": 5242880
}
```

```json
{
  "signedUrl": "https://app.example.com/api/buckets/uploads/files/uploads/user-123/profile-photo.jpg?token=eyJhbGciOiJIUzI1NiJ9...",
  "path": "uploads/user-123/profile-photo.jpg",
  "expiresAt": "2026-04-05T10:10:00Z",
  "method": "PUT",
  "headers": { "Content-Type": "image/jpeg" }
}
```

| Paramètre     | Type    | Requis | Par défaut         | Description                                         |
| ------------- | ------- | ------ | ------------------ | --------------------------------------------------- |
| `contentType` | string  | Non    | —                  | Type MIME requis, appliqué côté serveur sur le PUT. |
| `maxSize`     | integer | Non    | `10485760` (10 Mo) | Taille maximale de téléversement en octets.         |

| Comportement                                          | Résultat                                        |
| ----------------------------------------------------- | ----------------------------------------------- |
| Téléversement via l'URL PUT                           | Fichier stocké au chemin spécifié.              |
| Téléversement après expiration                        | `403 Forbidden`.                                |
| Mauvais type MIME (quand `contentType` est défini)    | `400 Bad Request`.                              |
| Dépasse `maxSize`                                     | `413 Payload Too Large`.                        |
| Réutiliser un jeton de téléversement pour télécharger | Rejeté — l'opération est encodée dans le jeton. |

## Signature par lot

Générez des URL signées pour jusqu'à 100 fichiers en une seule requête — utile pour afficher une page pleine d'images privées.

```http
POST /api/buckets/{bucket}/sign/batch
Content-Type: application/json
Authorization: Bearer <token>

{
  "files": [
    { "path": "photos/photo-1.jpg", "expiresIn": 3600 },
    { "path": "photos/photo-2.jpg", "expiresIn": 3600 },
    { "path": "documents/report.pdf", "expiresIn": 7200 }
  ]
}
```

Chaque fichier peut porter son propre `expiresIn`. Les fichiers inexistants sont renvoyés avec une entrée `error: "not_found"` (succès partiel plutôt que d'échouer tout le lot). Demander plus de 100 fichiers renvoie `400 Bad Request`.

## Récapitulatif des points de terminaison

| Méthode | Point de terminaison                           | Auth                                      |
| ------- | ---------------------------------------------- | ----------------------------------------- |
| `POST`  | `/api/buckets/{bucket}/sign`                   | Requise (session).                        |
| `POST`  | `/api/buckets/{bucket}/sign/batch`             | Requise (session).                        |
| `GET`   | `/api/buckets/{bucket}/files/{path}?token=...` | Aucune — le jeton est l'authentification. |
| `PUT`   | `/api/buckets/{bucket}/files/{path}?token=...` | Aucune — le jeton est l'authentification. |

Appeler l'un ou l'autre des points de terminaison `sign` sans session valide renvoie `401`.

## Contrôle d'accès

Qui peut signer est régi par deux couches indépendantes, toutes deux utilisant le format partagé `PermissionValueSchema` (`all` | `authenticated` | liste de rôles).

### Permissions par bucket

Définissez `sign` et `signUpload` sur le bucket.

```yaml
buckets:
  - name: documents
    permissions:
      sign: authenticated
      signUpload: [admin, editor]
      download: authenticated
      delete: [admin]

  - name: public-assets
    public: true
    permissions:
      sign: all
      signUpload: [admin]
      download: all
```

| Comportement                                     | Résultat                                        |
| ------------------------------------------------ | ----------------------------------------------- |
| L'appelant correspond à `permissions.sign`       | Peut générer des URL signées de téléchargement. |
| L'appelant correspond à `permissions.signUpload` | Peut générer des URL signées de téléversement.  |
| L'appelant ne correspond pas                     | `403 Forbidden`.                                |
| Le bucket n'a pas de bloc `permissions`          | Seul le rôle `admin` peut signer (par défaut).  |

### Permissions de stockage par rôle

Les rôles peuvent accorder ou révoquer indépendamment la signature via `storage.sign` / `storage.signUpload`.

```yaml
auth:
  roles:
    - name: member
      permissions:
        storage:
          sign: true
          signUpload: false
    - name: viewer
      permissions:
        storage:
          sign: false
          signUpload: false
```

Admin dispose des pleins droits de signature par défaut. Lorsqu'aucune permission de stockage n'est définie nulle part, la signature est par défaut réservée à admin.

## Intégration au champ de pièce jointe

Lorsqu'un [champ de pièce jointe](/fr/docs/attachment-fields) de table pointe vers un fichier privé, la réponse de l'API d'enregistrement intègre automatiquement une URL de téléchargement signée (expiration par défaut d'1 heure) :

```json
{
  "id": 1,
  "name": "Q1 Report",
  "attachment": {
    "path": "documents/q1-report.pdf",
    "filename": "q1-report.pdf",
    "size": 245760,
    "mimeType": "application/pdf",
    "signedUrl": "https://app.example.com/api/buckets/documents/files/documents/q1-report.pdf?token=...",
    "signedUrlExpiresAt": "2026-04-05T11:00:00Z"
  }
}
```

Les fichiers publics incluent une URL directe sans jeton. Les pièces jointes d'image peuvent porter des [paramètres de transformation](/fr/docs/image-transforms) à l'intérieur de l'URL signée.

## Pages associées

- [Présentation des buckets](/fr/docs/buckets-overview) — buckets, backends et le commutateur `public`.
- [Opérations sur les fichiers](/fr/docs/file-operations) — téléversement, téléchargement et suppression directs.
- [Transformations d'image](/fr/docs/image-transforms) — combiner les transformations avec les URL signées.
- [Champs de pièce jointe](/fr/docs/attachment-fields) — auto-signature dans les réponses d'enregistrement.
- [Variables d'environnement](/fr/docs/env-vars) — configuration des chemins publics et du niveau d'accès.
