# Vimine (Browser)

> Referenz der Klasse Vimine aus @vimine/sdk — Anmeldung, Sitzung, API-Aufrufe, Speicher.

URL: https://developers.vimine.network/de/sdk/browser/

```js
import { Vimine } from '@vimine/sdk';
const vimine = new Vimine(options);
```

`Vimine` übernimmt **Mit Vimine anmelden** (OAuth 2.1 + PKCE), die Sitzung des Mitglieds und die Aufrufe der
Mitglieder-API. Eine Instanz pro Client; legen Sie sie einmal an und verwenden Sie sie wieder.

## Optionen

| Option | Typ | Standard | |
|---|---|---|---|
| `clientId` | `string` | — | Ihre `vmc_…` (Pflicht) |
| `redirectUri` | `string` | — | die Seite, die `handleRedirect()` aufruft, für den Client registriert (Pflicht) |
| `scopes` | `Scope[]` | `['profile:read', 'membership:read']` | von `signIn()` angeforderte Berechtigungen |
| `storage` | `'session' \| 'local' \| 'memory' \| TokenStorage` | `'session'` | wo die Sitzung und die laufende Anmeldung liegen |
| `storageKey` | `string` | `vimine:<clientId>:` | Schlüsselpräfix im Speicher |
| `locale` | `Locale` | `'it'` | Sprache der Anmelde- und Einwilligungsseiten |
| `apiUrl` | `string` | `https://api.vimine.network` | Origin der API |
| `appUrl` | `string` | `https://app.vimine.network` | Origin der Anmeldeseiten |
| `fetch` | `typeof fetch` | `globalThis.fetch` | für Tests und Polyfills |
| `navigate` | `(url) => void` | `top.location.assign` | wie die Seite verlassen wird |

Der Konstruktor wirft einen `VimineError` (`invalid_options`) ohne `clientId` oder `redirectUri`.

## Anmeldung

### `signIn(options?)`

Schickt das **oberste Fenster** zur Anmelde- und Einwilligungsseite. Rufen Sie es aus einer Nutzergeste (einem Klick) auf. Aus einem
Cross-Origin-iframe heraus wird die Navigation abgelehnt (`framed`): Die Anmeldeseite wird nie in einem Frame gezeigt.

| Option | |
|---|---|
| `scopes` | Berechtigungen dieser Anmeldung |
| `prompt` | `'login'` (immer Zugangsdaten) oder `'consent'` (immer Einwilligung) |
| `returnTo` | Ihr eigener Zustand, lokal gespeichert und von `handleRedirect()` zurückgegeben — er wird nicht gesendet |

### `createSignInUrl(options?)`

Baut die Autorisierungs-URL und merkt sich die laufende Anmeldung (PKCE-Verifier und `state`), ohne zu
navigieren. Nützlich, wenn Sie den Link selbst darstellen.

### `hasRedirectParams(url?)`

`true`, wenn die URL (Standard: die aktuelle Seite) die Parameter einer Rückkehr trägt (`code`+`state` oder `error`).

### `handleRedirect({ url?, cleanUrl? })`

Auf der Callback-Seite: prüft den `state`, tauscht den Code gegen Tokens ein und speichert die Sitzung. Liefert
`{ session, returnTo }` oder `null`, wenn die URL keine Anmeldeparameter trägt. Mit `cleanUrl` (Standard
`true`) entfernt es `code`, `state` und `error` aus der Adressleiste.

Fehler (`VimineError.code`): `access_denied` und die anderen OAuth-Fehler, wenn das Mitglied nicht autorisiert hat;
`no_flow`, wenn in diesem Browser keine Anmeldung gestartet wurde (Speicher leer oder abgelaufen); `state_mismatch`, wenn die
Rückkehr nicht passt; `invalid_grant`, wenn der Code abgelaufen ist oder schon verwendet wurde.

## Sitzung

### `getSession()` · `isSignedIn()`

Die gespeicherte Sitzung oder `null`.

```ts
interface Session {
    accessToken: string;
    refreshToken: string | null;
    expiresAt: number;            // ms, absolut
    scope: Scope[];
    sub: string | null;           // die Kennung des Mitglieds für Ihren Verein (vms_…)
    mode?: 'live' | 'test';       // 'test' für Sitzungen eines Test-Clients
}
```

### `getAccessToken()`

Ein gültiges Access-Token, erneuert, wenn es innerhalb von 30 Sekunden abläuft. Wirft `not_signed_in` ohne Sitzung.

### `refresh()`

Rotiert die Sitzung mit dem Refresh-Token (eine einzige Anfrage, auch bei gleichzeitigen Aufrufen). Ist das
Refresh-Token nicht mehr verwendbar, wird die Sitzung gelöscht und der Fehler weitergeworfen.

### `signOut({ everywhere?, postLogoutRedirectUri?, state? })`

Widerruft das Refresh-Token (RFC 7009, nach bestem Bemühen) und vergisst die Sitzung. Mit `everywhere: true` schickt es den
Browser anschließend zu `/oauth/logout` der Vimine-App, das die Sitzung des Mitglieds beendet und zu
`postLogoutRedirectUri` zurückkehrt (Standard `redirectUri`; es muss auf einem registrierten Origin liegen).

### `onChange(listener)`

`listener(session | null)` bei jeder Anmeldung, Erneuerung und Abmeldung; mit `storage: 'local'` auch bei denen, die
in anderen Tabs geschehen. Liefert die Funktion zum Abmelden des Listeners.

## Mitglieder-API

### `request(path, init?)`

Ein authentifizierter Aufruf: `Authorization: Bearer`, JSON hin und zurück, Fehler als `VimineError`, eine Wiederholung nach einer
Erneuerung, wenn die Antwort `401` ist. `init.idempotencyKey` wird zum Header `Idempotency-Key`.

```js
const events = await vimine.request('/v1/me/membership/events?limit=5');
```

### `me`

| | Scope | |
|---|---|---|
| `me.get()` | `profile:read` | das Profil (`Me`), mit `codicefiscale_check`: wie die Person identifiziert ist (`null` = noch nicht) |
| `me.update(patch)` | `profile:write` | Telefon, Geschlecht und die Identifizierung: Geburtsdatum, Geburtsort und Geburtsland (`birth_country`), Steuernummer — optional, kann später ergänzt werden. Eine abweichende Steuernummer ergibt `422` mit einem Eintrag `codicefiscale_mismatch`; ist er `confirmable`, bestätigt die Person sie mit `codicefiscale_confirmed: true` ([Anleitung](https://developers.vimine.network/de/guides/members-without-cf/)) |

### `membership`

| | Scope | |
|---|---|---|
| `membership.get()` | `membership:read` | der Ausweis: `status`, `verdict`, `expires_at`, `statuto`, `card` |
| `membership.request(body, { idempotencyKey? })` | `membership:request` | Beitrittsantrag; fehlt ein Idempotenzschlüssel, wird einer erzeugt. `422 identification_required`, wenn das Profil nicht identifiziert ist: `err.errors` nennt, was fehlt, zu ergänzen mit `me.update()` |
| `membership.events({ limit?, cursor? })` | `membership:read` | Statusverlauf, seitenweise |
| `membership.statuto()` | `membership:read` | das geltende Statuto und die Annahme durch das Mitglied |
| `membership.acceptStatuto({ version, sha256? })` | `statuto:accept` | erfasst die Annahme |
| `membership.card()` | `membership:read` | das Ausweis-Token (QR) und sein Fenster; `409 not_active`, solange die Mitgliedschaft nicht aktiv ist |
| `membership.renew({ version, sha256? })` | `POST /v1/me/membership/renew` | Verlängerung durch das Mitglied: sofort, wenn kostenlos und ohne Genehmigung, sonst ein Antrag an das Sekretariat |
| `membership.setNetworkOptIn(bool)` | `PATCH /v1/me/membership/network-opt-in` | Einwilligung zum Netzwerk (`network:manage`) |
| `membership.googleWalletUrl()` | `GET /v1/me/membership/wallet/google` | ein Link „In Google Wallet speichern“; `409 not_active`, solange die Mitgliedschaft nicht aktiv ist |
| `membership.cardSvg()` | `GET /v1/me/membership/card.svg` | der Ausweis als SVG (mit dem QR-Code); `409 not_active`, solange die Mitgliedschaft nicht aktiv ist |
| `membership.emailCard()` | `POST /v1/me/membership/card/email` | schickt dem Mitglied seinen Ausweis per E-Mail |

### `events`

Die veröffentlichten Veranstaltungen des Vereins, einschließlich derer nur für Mitglieder (`events:read`). Die öffentlichen brauchen keine
Anmeldung: `vimine.public.events`.

| | |
|---|---|
| `events.list({ filter?, from?, to?, limit? })` | `filter`: `upcoming` (Standard), `past`, `cancelled`, `all` |
| `events.get(slug)` | Inhalte, Bilder, Anmeldearten mit freien Plätzen |
| `events.eligibility(slug)` | darf sich das Mitglied anmelden? pro Art, mit Gründen (`membership_required`, `sold_out`, …) |

### `registrations`

Die Anmeldungen des Mitglieds (`registrations:read` / `registrations:write`). Siehe die Anleitung [Veranstaltungen und Anmeldungen](https://developers.vimine.network/de/guides/events/).

| | |
|---|---|
| `registrations.create(slug, { registration_type, note?, quantity? }, opts?)` | kostenlose Art → `confirmed` mit dem QR-`token`; mit Beitrag → `reserved` mit `payment_instructions`; `422 not_eligible` mit den Gründen; `Idempotency-Key` wird erzeugt |
| `registrations.list({ filter? })` · `registrations.get(id)` | die Anmeldungen des Mitglieds (`filter: 'upcoming'`) |
| `registrations.cancel(id)` | storniert innerhalb des Fensters der Veranstaltung (danach `409 window_closed`) |
| `registrations.tokenPayload(id)` | der QR-Inhalt einer bestätigten Anmeldung oder `null` |

### `connections`

| | |
|---|---|
| `connections.list()` | die Apps, die das Mitglied in diesem Verein autorisiert hat |
| `connections.revoke(clientId)` | widerruft die Einwilligung für eine App (ihre Sitzungen enden) |

### `public`

Die öffentlichen Endpunkte ohne Zugangsdaten: siehe [`publicApi`](https://developers.vimine.network/de/sdk/public/).

## Speicher

| `storage` | Wo | Wann |
|---|---|---|
| `'session'` (Standard) | `sessionStorage`: pro Tab, weg, wenn er sich schließt | die sicherste Voreinstellung für einen öffentlichen Client |
| `'local'` | `localStorage`: zwischen Tabs geteilt, überlebt Neustarts; die Abmeldung erreicht die anderen Tabs | Apps, in denen das Mitglied erwartet, angemeldet zu bleiben |
| `'memory'` | nur im Speicher | SSR, Tests, Kiosk-Geräte |
| `TokenStorage` | ein eigenes Objekt `{ get, set, remove }` | Node, verschlüsselte Speicher, native Apps |

Wo der Browser-Speicher nicht verfügbar ist (privater Modus, `about:blank`), weicht das SDK auf den Arbeitsspeicher aus.
`MemoryStorage` wird exportiert.

## Hilfsfunktionen

- `createPkce()` → `{ verifier, challenge, method: 'S256' }`, `createVerifier()`, `createChallenge(verifier)`,
  `createState()`, `base64url(bytes)` — PKCE und `state` auf WebCrypto, falls Sie den Ablauf von Hand bauen.
- `DEFAULT_API_URL`, `DEFAULT_APP_URL`.
- `publicApi(options?)` — siehe [Öffentliche API](https://developers.vimine.network/de/sdk/public/).
- `VimineError` — siehe [Fehler](https://developers.vimine.network/de/sdk/errors/).
