wip: Sicherungs-Commit aller Änderungen seit April + Arbeitsstruktur (CLAUDE.md, ROADMAP.md, PROGRESS.md, Autopilot)

6 Wochen uncommittete Arbeit (Voice-Channels, LiveKit-Manager, Settings-Modal u.v.m.)
als ein WIP-Commit gesichert, damit nichts verloren geht und der Pi den aktuellen
Stand klonen kann. Thematische Aufarbeitung: siehe ROADMAP M0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPrAGBxBT6GfPXzeWQ4AXb
This commit is contained in:
Bernd Steckmeister
2026-07-03 05:47:18 +02:00
parent d706ace35f
commit 25ed765a03
195 changed files with 47784 additions and 1236 deletions
+241
View File
@@ -0,0 +1,241 @@
# 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
}
```