# 01 — Authentifizierung & Session

---

## Inhaltsverzeichnis
- [Login mit Passwort](#login-mit-passwort)
- [Login mit Token](#login-mit-token)
- [SSO / OAuth](#sso--oauth)
- [Register](#registrierung)
- [Session prüfen & wiederherstellen](#session-prüfen--wiederherstellen)
- [Logout](#logout)
- [Multi-Account / Device](#multi-account--device)
- [Homeserver ermitteln](#homeserver-ermitteln)
- [Flows & Login-Typen abfragen](#flows--login-typen-abfragen)

---

## Login mit Passwort

```dart
await client.checkHomeserver(Uri.parse('https://matrix.example.org'));

final loginResponse = await client.login(
  LoginType.mLoginPassword,
  password: 'geheimesPasswort',
  identifier: AuthenticationUserIdentifier(user: '@alice:example.org'),
  // oder nur Username (ohne Server):
  // identifier: AuthenticationUserIdentifier(user: 'alice'),
  initialDeviceDisplayName: 'Pyramid Android',
);
// loginResponse.accessToken, .deviceId, .userId
```

---

## Login mit Token

```dart
await client.checkHomeserver(Uri.parse('https://matrix.example.org'));

await client.login(
  LoginType.mLoginToken,
  token: 'einmaliger_login_token',
  initialDeviceDisplayName: 'Pyramid',
);
```

---

## SSO / OAuth

```dart
// Verfügbare Login-Flows abfragen
final flows = await client.getLoginFlows();
final hasSso = flows.flows?.any((f) => f.type == AuthenticationTypes.sso) ?? false;

// SSO-URL erzeugen (öffnet Browser/WebView)
final redirectUri = 'io.pyramid.app://login-callback';
final ssoUrl = client.homeserver!.toString() +
    '/_matrix/client/v3/login/sso/redirect?redirectUrl=${Uri.encodeComponent(redirectUri)}';
// → Uri im Browser öffnen, nach Redirect den Token extrahieren

// Nach Redirect mit Token einloggen
await client.login(
  LoginType.mLoginToken,
  token: tokenAusRedirectUrl,
  initialDeviceDisplayName: 'Pyramid',
);
```

---

## Registrierung

```dart
// Homeserver-Registrierung prüfen
await client.checkHomeserver(Uri.parse('https://matrix.example.org'));

// UIAA-Flow starten
try {
  await client.uiaRequestBackground(
    (auth) => client.register(
      username: 'neuernutzer',
      password: 'sicheresPasswort',
      initialDeviceDisplayName: 'Pyramid',
      auth: auth,
    ),
  );
} on MatrixException catch (e) {
  // e.error: M_USER_IN_USE, M_INVALID_USERNAME, etc.
}
```

---

## Session prüfen & wiederherstellen

```dart
// Nach client.init() prüfen ob Session noch gültig ist
if (client.isLogged()) {
  print('Eingeloggt als: ${client.userID}');
  print('Homeserver: ${client.homeserver}');
  print('Device ID: ${client.deviceID}');
  print('Access Token: ${client.accessToken}');
}

// Auf Login-State reagieren
client.onLoginStateChanged.stream.listen((LoginState state) {
  switch (state) {
    case LoginState.loggedIn:  // Session aktiv
    case LoginState.softLoggedOut: // Token abgelaufen, Re-Auth nötig
    case LoginState.loggedOut:     // Ausgeloggt
  }
});
```

---

## Logout

```dart
// Nur dieses Device
await client.logout();

// Alle Devices
await client.logoutAll();
```

---

## Multi-Account / Device

```dart
// Eigene Devices (Sessions) auflisten
final devices = await client.getDevices();
for (final device in devices ?? []) {
  print('${device.deviceId}: ${device.displayName} — ${device.lastSeenIp}');
}

// Device umbenennen
await client.updateDevice(
  deviceId: 'DEVICEID',
  displayName: 'Pyramid (Wohnzimmer)',
);

// Device löschen (andere Session killen)
await client.deleteDevice(
  'ANDERES_DEVICE_ID',
  auth: AuthenticationPassword(
    identifier: AuthenticationUserIdentifier(user: client.userID!),
    password: 'meinPasswort',
  ),
);

// Mehrere Devices auf einmal löschen
await client.deleteDevices(
  ['DEVICE1', 'DEVICE2'],
  auth: AuthenticationPassword(...),
);
```

---

## Homeserver ermitteln

```dart
// .well-known auflösen (Autodiscovery)
final discovery = await client.checkHomeserver(
  Uri.parse('https://example.org'), // Domain reicht, SDK findet Matrix-Server
);
// client.homeserver ist jetzt gesetzt

// Direkt mit bekannter HS-URL
await client.checkHomeserver(Uri.parse('https://matrix.example.org'));

// Homeserver-Versioninfo
final serverVersion = await client.getVersions();
print(serverVersion.versions); // ['v1.1', 'v1.2', ...]
```

---

## Flows & Login-Typen abfragen

```dart
final response = await client.getLoginFlows();
for (final flow in response.flows ?? []) {
  switch (flow.type) {
    case AuthenticationTypes.password: // m.login.password
    case AuthenticationTypes.sso:      // m.login.sso
    case AuthenticationTypes.token:    // m.login.token
    case 'm.login.cas':                // Legacy-CAS
  }
}

// SSO-Provider Details (für Brand-Buttons im UI)
final ssoProviders = response.flows
    ?.where((f) => f.type == AuthenticationTypes.sso)
    .expand((f) => f.identityProviders ?? [])
    .toList();
// ssoProvider.id, .name, .brand, .icon
```

---

## UIAA (User-Interactive Auth) Flows

```dart
// Generisches UIAA-Handling (z.B. für Account-Löschung)
await client.uiaRequestBackground(
  (auth) => client.deactivateAccount(auth: auth, eraseData: false),
);

// Passwort ändern (braucht Re-Auth)
await client.uiaRequestBackground(
  (auth) => client.changePassword(
    'neuesPasswort',
    auth: auth,
    oldPassword: 'altesPasswort', // optional, falls Server es erlaubt
  ),
);
```

---

## Fehlerbehandlung

```dart
try {
  await client.login(...);
} on MatrixException catch (e) {
  switch (e.error) {
    case MatrixError.M_FORBIDDEN:       // Falsches Passwort
    case MatrixError.M_USER_IN_USE:     // Username vergeben
    case MatrixError.M_INVALID_USERNAME:
    case MatrixError.M_LIMIT_EXCEEDED:  // Rate-Limiting
    case MatrixError.M_UNKNOWN:
  }
  print(e.errorMessage); // Menschenlesbarer Text vom Server
}
```
