# Se connecter avec Vimine

> Le flux OAuth 2.1 + PKCE du clic au callback, la session, les appels à l’API et la déconnexion, avec et sans le SDK.

URL: https://developers.vimine.network/fr/guides/sign-in/

Les membres d’une association se connectent à votre site avec leur compte Vimine et vous autorisent, par un
consentement explicite, à utiliser certaines de leurs données. C’est OAuth 2.1 avec PKCE : le mot de passe n’est
jamais saisi ailleurs que sur la page Vimine, jamais sur la vôtre ; votre site reçoit des jetons limités aux
autorisations accordées.

:::note[Avant de commencer]
Il vous faut un client **Site web** ou **Application native** avec votre `redirect_uri` enregistrée — voir
[Enregistrer un client](https://developers.vimine.network/fr/start/register-a-client/) — et le SDK installé ([Installer le SDK](https://developers.vimine.network/fr/start/install/)).
Pour développer sans toucher aux données réelles, utilisez un client de **test** et la [sandbox](https://developers.vimine.network/fr/guides/sandbox/).
:::

## Le flux

1. À partir d’un **clic** du membre, votre site envoie le navigateur (la fenêtre principale, jamais une iframe) vers
   la page d’autorisation :
   ```
   https://app.vimine.network/authorize
       ?client_id=vmc_…&redirect_uri=https://www.example.org/vimine/callback
       &response_type=code&state=…&code_challenge=…&code_challenge_method=S256
       &scope=profile:read%20membership:read
   ```
2. Le membre se connecte (s’il ne l’est pas déjà) et voit **à qui** il accorde **quoi**. Il confirme ou annule.
3. Vimine renvoie le navigateur vers votre `redirect_uri` avec `code` et `state` (ou `error=access_denied`).
4. Votre site échange le code sur `POST https://api.vimine.network/v1/oauth/token`
   (`grant_type=authorization_code`, `code`, `code_verifier`, `client_id`, `redirect_uri`) et reçoit un
   jeton d’accès (10 minutes) et un jeton de rafraîchissement (qui change à chaque usage ; 30 jours, 14 d’inactivité).

Le SDK fait tout cela pour vous.

## Avec le SDK

```js title="n’importe quelle page"
import { Vimine } from '@vimine/sdk';

const vimine = new Vimine({
    clientId: 'vmc_…',
    redirectUri: 'https://www.example.org/vimine/callback',
    scopes: ['profile:read', 'membership:read']
});

// depuis un clic : `returnTo` est votre propre état, vous le récupérez après le callback
document.querySelector('#sign-in').addEventListener('click', () => vimine.signIn({ returnTo: location.pathname }));
```

```js title="page de callback"
const result = await vimine.handleRedirect();   // null quand l’URL ne porte aucun paramètre de connexion
if (result) location.replace(result.returnTo || '/');
```

Options de `signIn()` :

| Option | Effet |
|---|---|
| `scopes` | les autorisations à demander pour cette connexion (par défaut : celles du constructeur, ou `profile:read membership:read`) |
| `prompt: 'login'` | force le formulaire d’identifiants même avec une session Vimine active |
| `prompt: 'consent'` | affiche toujours l’écran de consentement |
| `returnTo` | votre propre état (par exemple la page d’où le membre est parti), conservé localement et renvoyé par `handleRedirect()` |

`handleRedirect()` lève une [`VimineError`](https://developers.vimine.network/fr/sdk/errors/) avec le `code` `access_denied` quand le membre a
annulé, `state_mismatch` ou `no_flow` quand le retour ne correspond pas à une connexion lancée depuis ce navigateur.

## La session

- Les jetons vivent dans `sessionStorage` (par onglet). `storage: 'local'` les partage entre les onglets et synchronise
  la déconnexion ; `'memory'` ne les conserve jamais. Vous pouvez passer un adaptateur à vous.
- `vimine.getAccessToken()` rafraîchit de lui-même 30 secondes avant l’expiration ; un appel qui reçoit 401 est
  réessayé une fois après le rafraîchissement. Un jeton de rafraîchissement **réutilisé** (copié, par exemple) révoque
  toute la famille de sessions.
- `vimine.onChange(session => …)` signale la connexion, le rafraîchissement et la déconnexion.
- `vimine.getSession().sub` est l’identifiant du membre **pour votre association** (`vms_…`) : stable dans le temps,
  différent dans chaque association, jamais un identifiant interne.

## Ce que vous pouvez demander

```js
await vimine.me.get();                                     // profile:read
await vimine.me.update({ phone: '+39 …' });                // profile:write
await vimine.membership.get();                             // membership:read — status, verdict, expires_at, statuto, card
await vimine.membership.events({ limit: 20 });             // historique des états
await vimine.membership.statuto();                         // le statuto en vigueur et si le membre l’a accepté
await vimine.membership.acceptStatuto({ version: 3 });     // statuto:accept
await vimine.membership.request({ accept_statuto: { version: 3 }, privacy_notice: { version: 2 } });   // membership:request → carte en attente
await vimine.membership.card();                            // le jeton de la carte (QR) et sa fenêtre
await vimine.legal.acceptances();                          // legal:read → ce qu’il a accepté et ce qui reste en attente
await vimine.legal.record({ kind: 'privacy_notice', version: 2 });   // legal:accept → prendre acte de la notice de l’association
await vimine.connections.list();  await vimine.connections.revoke('vmc_…');
```
:::note[Documents juridiques]
`legal.acceptances()` liste les acceptations de la personne et, sous `pending`, les documents encore à confirmer dans
leur version en vigueur : les conditions du compte et la notice d’information sur la vie privée de la plateforme
(`platform`), la notice de cette association à ses membres (`privacy_notice`). Affichez le texte (`GET /v1/legal/{kind}`,
`GET /v1/aps/{slug}/privacy-notice`) et enregistrez la confirmation avec `legal.record()` : la version doit être celle
en vigueur, sinon `422 <kind>_outdated`.
:::
:::note[Activation]
Ce qu’il advient d’une demande (`membership.request()`) dépend de **la façon dont l’association admet ses membres** — `admission` dans le profil public, expliqué dans [Admission et activation](https://developers.vimine.network/fr/guides/admission/). Si c’est son conseil qui décide, elle reste `pending` avec des `pending_reasons` ordonnées (`approval`, puis `payment`) et la cotisation n’est payée qu’après l’admission (`payment_open`) ; sinon elle naît **active** quand le type est gratuit, ou quand la cotisation est enregistrée. Une adhésion active peut porter un `valid_from` dans le futur : la carte fait foi à l’entrée à partir de ce moment. Montrez au membre `quota` et `payment_instructions` chaque fois que `payment` est présent.
:::

`verdict` est ce qui compte à l’entrée : `valid`, `expired`, `statuto_required` (nouvelle acceptation en retard) ou
`inactive`. Une personne qui n’est pas encore membre peut tout de même se connecter et utiliser `membership.request` :
le consentement aux autorisations d’adhésion est permis aussi aux non-membres ; les autres autorisations exigent une
carte dans cette association.

Chaque méthode est détaillée dans [`Vimine`](https://developers.vimine.network/fr/sdk/browser/) ; les réponses dans [Types](https://developers.vimine.network/fr/sdk/types/).

## Se déconnecter

```js
await vimine.signOut();                        // révoque la session API et l’oublie
await vimine.signOut({ everywhere: true });    // met aussi fin à la session Vimine du membre et revient sur votre site
```

Avec `everywhere`, le navigateur passe par
`https://app.vimine.network/oauth/logout?client_id=…&post_logout_redirect_uri=…` ; l’adresse de retour doit se
trouver sur une origine enregistrée pour le client. Quand le membre se déconnecte de Vimine, toutes les sessions API
de ses applications sont révoquées (déconnexion unique) ; il en va de même quand il révoque une application depuis
**Applications connectées** ou qu’il réinitialise son mot de passe.

## Sans le SDK

Si vous ne pouvez pas utiliser le SDK (un autre langage, une plateforme low-code), le flux est de l’OAuth 2.1 standard.

**Autorisation** — `GET https://app.vimine.network/authorize`

| Paramètre | |
|---|---|
| `client_id` | votre `vmc_…` |
| `redirect_uri` | l’une de celles enregistrées, à l’identique |
| `response_type` | `code` |
| `state` | valeur aléatoire, à comparer au retour |
| `code_challenge`, `code_challenge_method` | PKCE, `S256` uniquement |
| `scope` | autorisations séparées par des espaces |
| `prompt` | facultatif : `login`, `consent` |
| `ui_locales` | facultatif : une des langues de Vimine — `it` (par défaut), `en`, `zh-Hans`, `zh-Hant`, `fr`, `es`, `de`, `pt-BR` |

**Échange** — `POST https://api.vimine.network/v1/oauth/token`, corps `application/x-www-form-urlencoded` :

```
grant_type=authorization_code&code=…&code_verifier=…&client_id=vmc_…&redirect_uri=…
```

```json title="réponse"
{ "access_token": "…", "token_type": "Bearer", "expires_in": 600, "refresh_token": "…",
  "scope": "profile:read membership:read", "sub": "vms_…" }
```

**Rafraîchissement** — même endpoint avec `grant_type=refresh_token&refresh_token=…&client_id=…` : la réponse porte
un **nouveau** jeton de rafraîchissement (l’ancien reste valable 15 secondes pour les requêtes concurrentes, puis le
réutiliser révoque toute la famille).

**Révocation** — `POST /v1/oauth/revoke` avec `token=…&token_type_hint=refresh_token` (RFC 7009).

**Découverte** — `GET https://api.vimine.network/.well-known/oauth-authorization-server` (RFC 8414) liste les
endpoints, les scopes et les méthodes prises en charge.

Les erreurs de ces endpoints suivent le format OAuth : `{ "error": "invalid_grant", "error_description": "…" }`.
