
# Atterrissage après connexion

Après la connexion, différents utilisateurs devraient atterrir sur différentes pages — un administrateur sur l'accueil d'administration, un client sur son enregistrement (ou un sélecteur lorsqu'il en a plusieurs). Sovrium exprime cela comme une **configuration d'authentification**, et non comme une logique de redirection par page : chaque rôle déclare un `defaultLanding`, le bloc `auth` déclare un point de montage `landingPath`, et `noAccessPath` gère le cas non concordant.

```yaml
auth:
  strategies:
    - type: emailAndPassword
  scopeTables: [clients]
  defaultRole: customer-admin
  landingPath: /portal
  noAccessPath: /403
  roles:
    - name: engineer
      defaultLanding: /admin
    - name: customer-admin
      defaultLanding: /portal/clients/$currentUser.assignments.clients[0]
      pickerLanding: /portal/select/clients
```

## Champs de configuration

Deux champs sur `auth`, deux sur chaque rôle :

| Champ            | Niveau    | Description                                                                                                                                                                                                                              |
| ---------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `landingPath`    | `auth`    | Chemin de montage du résolveur de moteur. Les sessions qui naviguent ici sont redirigées vers le `defaultLanding` du rôle correspondant. Doit commencer par `/`. Obligatoire dès qu'un rôle définit `defaultLanding` ou `pickerLanding`. |
| `noAccessPath`   | `auth`    | Chemin de repli lorsqu'aucun `defaultLanding` de rôle ne correspond à la session. Doit commencer par `/`. Par défaut `/403`.                                                                                                             |
| `defaultLanding` | `roles[]` | URL d'atterrissage par rôle. Doit commencer par `/`. Peut contenir au plus un jeton `$currentUser.assignments.<table>[0]`.                                                                                                               |
| `pickerLanding`  | `roles[]` | Repli multi-enregistrements pour un `defaultLanding` _à modèle_. Doit commencer par `/`. Ne doit **pas** contenir de jeton d'affectation.                                                                                                |

:::callout
`landingPath` est **contrôlé par l'utilisateur** — choisissez `/portal`, `/dashboard`, `/home`, ou n'importe quoi d'autre. Il n'y a pas de convention de moteur. Il en va de même pour `pickerLanding` : `/portal/select/clients`, `/companies-picker`, comme bon vous semble.
:::

## Logique de résolution

Lorsqu'une session authentifiée navigue vers `auth.landingPath`, le moteur parcourt `auth.roles[]` dans l'**ordre de déclaration** et applique le premier rôle que l'utilisateur détient. Pour le `defaultLanding` de ce rôle :

| Forme de `defaultLanding`                  | Nombre d'affectations pour la `<table>` à modèle | Action du moteur                                   |
| ------------------------------------------ | ------------------------------------------------ | -------------------------------------------------- |
| URL simple (sans jeton)                    | s.o.                                             | Rediriger vers `defaultLanding` sans condition.    |
| À modèle (un jeton)                        | Exactement une affectation                       | Substituer l'ID d'affectation, rediriger là.       |
| À modèle (un jeton)                        | Plus d'une affectation                           | Rediriger vers le `pickerLanding` du rôle.         |
| Aucun rôle concordant / aucune affectation | s.o.                                             | Rediriger vers `noAccessPath` (par défaut `/403`). |

## Interpolation `$currentUser.assignments`

Le jeton `$currentUser.assignments.<table>` se résout en l'ensemble des ID d'enregistrement issus des lignes `user_access` de l'utilisateur pour `<table>` (une [table de périmètre](/fr/docs/auth-sessions)). Il apparaît de deux manières distinctes :

**Dans une URL `defaultLanding`** — la forme à enregistrement unique `$currentUser.assignments.<table>[0]` substitue le _premier_ ID d'affectation, produisant un lien direct vers cet enregistrement :

```yaml
roles:
  - name: customer-admin
    defaultLanding: /portal/clients/$currentUser.assignments.clients[0]
    pickerLanding: /portal/select/clients
```

Un rôle peut contenir **au plus un** tel jeton (le résolveur ne peut pas déduire quel périmètre compter avec deux). Un `pickerLanding` ne doit en contenir **aucun** — par définition, le cas multi-enregistrements n'a pas d'ID unique à substituer.

**Dans un `dataSource.filter`** — la forme non indexée `$currentUser.assignments.<table>` se résout en la liste complète des ID, de sorte que la page de sélection liste exactement les enregistrements que l'utilisateur peut atteindre :

```yaml
pages:
  - name: client-picker
    path: /portal/select/clients
    access: authenticated
    components:
      - type: list
        props: { id: clients }
        dataSource:
          table: clients
          filter:
            - field: id
              operator: in
              value: $currentUser.assignments.clients
        children:
          - type: text
            content: $record.name
```

## La page `landingPath` est le garde d'accès

`landingPath` doit être adossé à une **page co-localisée** déclarée au même `path`. Cette page est le garde d'accès non authentifié : son bloc `access` renvoie les visiteurs anonymes vers `/login` avant que le résolveur d'atterrissage ne s'exécute. Pour les visiteurs authentifiés, le résolveur redirige avant que cette page (généralement vide) ne soit rendue.

```yaml
auth:
  landingPath: /portal
  # ...

pages:
  - name: portal-landing
    path: /portal
    access:
      require: authenticated
      redirectTo: /login
    components: [] # never rendered for authed users — resolver redirects first
```

:::callout
Oublier la page de garde co-localisée est l'erreur la plus courante : `landingPath: /portal` sans page `/portal` laisse les visiteurs anonymes sans protection. Associez toujours le chemin de montage à une page qui exige l'authentification.
:::

## Exemple complet

```yaml
auth:
  strategies:
    - type: emailAndPassword
  scopeTables: [clients]
  defaultRole: customer-admin
  landingPath: /portal
  noAccessPath: /403
  roles:
    - name: engineer
      defaultLanding: /admin
    - name: customer-admin
      defaultLanding: /portal/clients/$currentUser.assignments.clients[0]
      pickerLanding: /portal/select/clients

pages:
  - name: portal-landing
    path: /portal
    access: { require: authenticated, redirectTo: /login }
    components: []
  - name: client-detail
    path: /portal/clients/:id
    access: authenticated
    components: []
  - name: client-picker
    path: /portal/select/clients
    access: authenticated
    components: []
  - name: admin-home
    path: /admin
    access: [engineer]
    components: []
  - name: forbidden
    path: /403
    access: authenticated
    components: []
```

Dans cette application : un `engineer` atterrit sur `/admin` ; un `customer-admin` avec un seul client atterrit sur la page de détail de ce client ; avec plusieurs, sur le sélecteur ; et quiconque que le résolveur ne peut pas placer atteint `/403`.

## Pages associées

- [Rôles & RBAC](/fr/docs/auth-roles-rbac) — là où `defaultLanding` / `pickerLanding` sont définis sur les rôles.
- [Sessions](/fr/docs/auth-sessions) — `scopeTables`, `user_access` et `$currentUser.activeAssignment`.
- [Pages](/fr/docs/pages-overview) — `path`, `access` et `dataSource.filter` de page.
