# Mit Vimine anmelden

> Der Ablauf mit OAuth 2.1 + PKCE vom Klick bis zum Callback, die Sitzung, die API-Aufrufe und die Abmeldung, mit und ohne SDK.

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

Die Mitglieder eines Vereins melden sich mit ihrem Vimine-Konto auf Ihrer Website an und erlauben Ihnen mit einer
ausdrücklichen Einwilligung, einen Teil ihrer Daten zu nutzen. Es ist OAuth 2.1 mit PKCE: Das Passwort wird nur auf der
Vimine-Seite eingegeben, nie auf Ihrer; Ihre Website erhält Tokens, die auf die erteilten Berechtigungen beschränkt sind.

:::note[Bevor Sie anfangen]
Sie brauchen einen Client vom Typ **Website** oder **Native App** mit registrierter `redirect_uri` — siehe
[Einen Client registrieren](https://developers.vimine.network/de/start/register-a-client/) — und das installierte SDK ([Das SDK installieren](https://developers.vimine.network/de/start/install/)).
Um zu entwickeln, ohne echte Daten zu berühren, verwenden Sie einen **Test**-Client und die [Sandbox](https://developers.vimine.network/de/guides/sandbox/).
:::

## Der Ablauf

1. Auf den **Klick** eines Mitglieds schickt Ihre Website den Browser (das oberste Fenster, nie einen iframe) zur
   Autorisierungsseite:
   ```
   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. Das Mitglied meldet sich an (falls noch nicht geschehen) und sieht, **wem** es **was** erlaubt. Es bestätigt oder bricht ab.
3. Vimine schickt den Browser mit `code` und `state` (oder `error=access_denied`) zu Ihrer `redirect_uri` zurück.
4. Ihre Website tauscht den Code bei `POST https://api.vimine.network/v1/oauth/token` ein
   (`grant_type=authorization_code`, `code`, `code_verifier`, `client_id`, `redirect_uri`) und erhält ein
   Access-Token (10 Minuten) und ein Refresh-Token (rotierend; 30 Tage, 14 bei Inaktivität).

Das SDK erledigt all das für Sie.

## Mit dem SDK

```js title="beliebige Seite"
import { Vimine } from '@vimine/sdk';

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

// auf einen Klick: `returnTo` ist Ihr eigener Zustand, Sie erhalten ihn nach dem Callback zurück
document.querySelector('#sign-in').addEventListener('click', () => vimine.signIn({ returnTo: location.pathname }));
```

```js title="Callback-Seite"
const result = await vimine.handleRedirect();   // null, wenn die URL keine Anmeldeparameter trägt
if (result) location.replace(result.returnTo || '/');
```

Optionen von `signIn()`:

| Option | Wirkung |
|---|---|
| `scopes` | die Berechtigungen, die bei dieser Anmeldung angefordert werden (Standard: die des Konstruktors oder `profile:read membership:read`) |
| `prompt: 'login'` | erzwingt das Anmeldeformular, auch bei laufender Vimine-Sitzung |
| `prompt: 'consent'` | zeigt immer die Einwilligungsseite |
| `returnTo` | Ihr eigener Zustand (zum Beispiel die Seite, von der das Mitglied kam), lokal gespeichert und von `handleRedirect()` zurückgegeben |

`handleRedirect()` wirft einen [`VimineError`](https://developers.vimine.network/de/sdk/errors/) mit `code` `access_denied`, wenn das Mitglied
abgebrochen hat, `state_mismatch` oder `no_flow`, wenn die Rückkehr zu keiner Anmeldung passt, die in diesem Browser gestartet wurde.

## Die Sitzung

- Die Tokens liegen in `sessionStorage` (pro Tab). `storage: 'local'` teilt sie zwischen Tabs und synchronisiert die Abmeldung;
  `'memory'` speichert sie nie dauerhaft. Sie können einen eigenen Adapter übergeben.
- `vimine.getAccessToken()` erneuert selbst 30 Sekunden vor dem Ablauf; ein Aufruf, der 401 erhält, wird nach der Erneuerung
  einmal wiederholt. Ein **wiederverwendetes** Refresh-Token (etwa ein kopiertes) widerruft die ganze Sitzungsfamilie.
- `vimine.onChange(session => …)` meldet Anmeldung, Erneuerung und Abmeldung.
- `vimine.getSession().sub` ist die Kennung des Mitglieds **für Ihren Verein** (`vms_…`): dauerhaft stabil,
  in jedem Verein anders, nie eine interne ID.

## Was Sie abfragen können

```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 });             // Statusverlauf
await vimine.membership.statuto();                         // das geltende Statuto und ob das Mitglied es akzeptiert hat
await vimine.membership.acceptStatuto({ version: 3 });     // statuto:accept
await vimine.membership.request({ accept_statuto: { version: 3 }, privacy_notice: { version: 2 } });   // membership:request → Mitgliedschaft in Prüfung
await vimine.membership.card();                            // das Ausweis-Token (QR) und sein Fenster
await vimine.legal.acceptances();                          // legal:read → was die Person akzeptiert hat und was noch aussteht
await vimine.legal.record({ kind: 'privacy_notice', version: 2 });   // legal:accept → Kenntnisnahme der Datenschutzhinweise des Vereins
await vimine.connections.list();  await vimine.connections.revoke('vmc_…');
```
:::note[Rechtliche Dokumente]
`legal.acceptances()` listet die Annahmen der Person und, unter `pending`, die Dokumente, die in ihrer geltenden
Version noch zu bestätigen sind: die Nutzungsbedingungen des Kontos und die Datenschutzerklärung der Plattform (`platform`), die
Datenschutzhinweise dieses Vereins für seine Mitglieder (`privacy_notice`). Zeigen Sie den Text (`GET /v1/legal/{kind}`, `GET /v1/aps/{slug}/privacy-notice`) und erfassen Sie
die Bestätigung mit `legal.record()`: Die Version muss die geltende sein, sonst `422 <kind>_outdated`.
:::
:::note[Aktivierung]
Was mit einem Antrag (`membership.request()`) geschieht, hängt davon ab, **wie der Verein seine Mitglieder aufnimmt** — `admission` im öffentlichen Profil, erklärt in [Aufnahme und Aktivierung](https://developers.vimine.network/de/guides/admission/). Entscheidet sein Vorstand, bleibt er `pending` mit geordneten `pending_reasons` (`approval`, dann `payment`), und der Mitgliedsbeitrag wird erst nach der Aufnahme gezahlt (`payment_open`); andernfalls entsteht die Mitgliedschaft **aktiv**, wenn die Art kostenlos ist, oder wird es, sobald der Beitrag erfasst ist. Eine aktive Mitgliedschaft kann ein `valid_from` in der Zukunft tragen: Ab dann gilt der Ausweis am Einlass. Zeigen Sie dem Mitglied `quota` und `payment_instructions`, wann immer `payment` vorhanden ist.
:::

`verdict` ist, was am Einlass zählt: `valid`, `expired`, `statuto_required` (erneute Annahme überfällig) oder
`inactive`. Wer noch nicht Mitglied ist, kann sich trotzdem anmelden und `membership.request` nutzen: Die Einwilligung in die
Beitrittsberechtigungen ist auch Nichtmitgliedern erlaubt; die anderen Berechtigungen setzen einen Ausweis in diesem Verein voraus.

Jede Methode ist in [`Vimine`](https://developers.vimine.network/de/sdk/browser/) beschrieben; die Antworten in [Typen](https://developers.vimine.network/de/sdk/types/).

## Abmelden

```js
await vimine.signOut();                        // widerruft die API-Sitzung und vergisst sie
await vimine.signOut({ everywhere: true });    // beendet zusätzlich die Vimine-Sitzung des Mitglieds und kehrt zu Ihrer Website zurück
```

Mit `everywhere` geht der Browser über
`https://app.vimine.network/oauth/logout?client_id=…&post_logout_redirect_uri=…`; die Rückkehradresse muss
auf einem für den Client registrierten Origin liegen. Wenn sich das Mitglied bei Vimine abmeldet, wird jede API-Sitzung seiner Apps
widerrufen (Single Sign-out); dasselbe geschieht, wenn es eine App unter **Verbundene Apps** widerruft oder sein
Passwort zurücksetzt.

## Ohne das SDK

Wenn Sie das SDK nicht verwenden können (eine andere Sprache, eine Low-Code-Plattform), ist der Ablauf Standard-OAuth 2.1.

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

| Parameter | |
|---|---|
| `client_id` | Ihre `vmc_…` |
| `redirect_uri` | eine der registrierten, identisch |
| `response_type` | `code` |
| `state` | Zufallswert, bei der Rückkehr zu vergleichen |
| `code_challenge`, `code_challenge_method` | PKCE, nur `S256` |
| `scope` | durch Leerzeichen getrennte Berechtigungen |
| `prompt` | optional: `login`, `consent` |
| `ui_locales` | optional: eine der Sprachen von Vimine — `it` (Standard), `en`, `zh-Hans`, `zh-Hant`, `fr`, `es`, `de`, `pt-BR` |

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

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

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

**Erneuerung** — derselbe Endpunkt mit `grant_type=refresh_token&refresh_token=…&client_id=…`: Die Antwort enthält
ein **neues** Refresh-Token (das alte bleibt für gleichzeitige Anfragen noch 15 Sekunden gültig; danach widerruft seine Wiederverwendung
die ganze Familie).

**Widerruf** — `POST /v1/oauth/revoke` mit `token=…&token_type_hint=refresh_token` (RFC 7009).

**Discovery** — `GET https://api.vimine.network/.well-known/oauth-authorization-server` (RFC 8414) listet
Endpunkte, Scopes und unterstützte Verfahren.

Die Fehler dieser Endpunkte folgen dem OAuth-Format: `{ "error": "invalid_grant", "error_description": "…" }`.
