fix: Geräte-Pairing - Schlüssel-Code stimmte nie mit dem angezeigten Code überein

SnapshotService.CreateAndUploadAsync lädt in zwei Schritten hoch: Schritt 1
holt einen Code vom Server, Schritt 2 verschlüsselt den Sync-Schlüssel mit
diesem Code und lädt erneut hoch. SnapshotStore.Store() vergab bei jedem
Aufruf bedingungslos einen neuen Zufallscode - der dem Nutzer am Ende
angezeigte Code war dadurch nie derselbe, mit dem der Schlüssel tatsächlich
verschlüsselt wurde. Jede Kopplung musste deterministisch an der
Schlüssel-Entschlüsselung scheitern.

SnapshotUploadRequest bekommt ein optionales Code-Feld; Store() aktualisiert
bei vorhandenem, passendem Code denselben Eintrag statt einen neuen mit
neuem Code anzulegen. Betrifft LehrerApp.Api - der Server muss neu deployt
werden.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 21:34:43 +02:00
co-authored by Claude Sonnet 5
parent 26461bd2f2
commit dc21cb2319
5 changed files with 129 additions and 1 deletions
+16
View File
@@ -19,6 +19,22 @@ public class SnapshotStore(string dataPath) : IDisposable
public SnapshotUploadResponse Store(string userId, SnapshotUploadRequest req)
{
// Zweiter Upload-Schritt (Schlüssel nachreichen, mit dem im ersten Schritt vergebenen Code
// verschlüsselt): denselben Eintrag aktualisieren statt einen neuen mit neuem Code
// anzulegen — sonst würde der Schlüssel dauerhaft mit dem falschen Code verknüpft bleiben
// und die Entschlüsselung auf dem Empfängergerät schlägt fehl (Bug, siehe TODO.md 10.3.1).
if (!string.IsNullOrEmpty(req.Code))
{
var existing = Col.FindOne(e => e.UserId == userId && e.Code == req.Code.ToUpperInvariant());
if (existing is not null)
{
existing.EncryptedPayload = req.EncryptedPayload;
existing.EncryptedSyncKey = req.EncryptedSyncKey;
Col.Update(existing);
return new() { Code = existing.Code, ExpiresAt = existing.ExpiresAt };
}
}
Col.DeleteMany(e => e.UserId == userId);
var code = NewCode();
var entry = new SnapshotEntry { Code = code, UserId = userId,