Files
pyramid/docs/matrix-sdk/01-auth.md
T
Bernd Steckmeister 25ed765a03 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
2026-07-03 05:47:18 +02:00

5.6 KiB

01 — Authentifizierung & Session


Inhaltsverzeichnis


Login mit Passwort

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

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

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

SSO / OAuth

// 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

// 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

// 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

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

// Alle Devices
await client.logoutAll();

Multi-Account / Device

// 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

// .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

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

// 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

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
}