25ed765a03
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
242 lines
5.6 KiB
Markdown
242 lines
5.6 KiB
Markdown
# 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
|
|
}
|
|
```
|