Restaurant Modele

Web-Login mit MY WALL3T auf der eigenen Webseite

Diese Seite richtet sich an Entwickler von Anbietern. Sie beschreibt, wie eine Webseite ihre Besucher mit der MY WALL3T App anmeldet und danach Mitgliedschaften und Punkte des Besuchers bei diesem einen Anbieter liest. Der Mitgliederbereich dieser Demo ist genau so gebaut.

1. Ablauf

Der Besucher bestätigt die Anmeldung in seiner MY WALL3T App. Die Webseite bekommt kein Passwort und keine Wallet-Adresse, sondern ein vom MY-WALL3T-Server signiertes Token. Der Baustein (Abschnitt 2) erledigt alle Schritte selbst; die Übersicht hilft beim Verstehen und bei der Fehlersuche. Alle Pfade relativ zu https://mywall3t.integ.kurz.digital/api, … steht für /branch-login/v1/{Kürzel}.

Am Computer: QR-Code

Webseite (Browser)                   MY-WALL3T-Server                    App des Besuchers
1 POST …/logins  ─────────────────►  Origin registriert?
  {codeChallenge,                    legt Login an (5 Minuten gültig)
   codeChallengeMethod:"S256",       ◄── 201 {loginId, expiresAt,
   returnUrl?}                                qrPayload, appLink}
2 zeigt den QR-Code
3                                                                        scannt, sieht Anbieter
                                                                         und Webseite, bestätigt
                                                                         oder lehnt ab
4 POST …/logins/{loginId}/token ──►  prüft den PKCE-Verifier
  {codeVerifier}                     ◄── 202 {status:"PENDING"}       (weiter warten)
                                         200 {accessToken, tokenType:"Bearer", expiresIn}
                                         200 {status:"REJECTED"}      (abgelehnt)
                                         200 {status:"CONTINUED"}     (im neuen Tab weiter)
                                         422 BRANCH_LOGIN_EXPIRED     (abgelaufen/verbraucht)
5 GET …/me  ──────────────────────►  prüft das Token
  Authorization: Bearer <Token>     ◄── 200 Datensatz (Abschnitt 3)
6 POST …/logout  ─────────────────►  sperrt das Token     ◄── 204

Am Handy: „Mit MY WALL3T anmelden“

Ist eine Rücksprung-Adresse registriert und im Baustein gesetzt, zeigt er neben dem QR-Code einen Knopf. Er öffnet die App; nach „Anmelden und weiter zur Webseite“ öffnet die App die registrierte Rücksprung-Adresse mit einem Einmal-Code im Fragment (#bl=…). Der Baustein holt damit das Token ab und entfernt das Fragment sofort aus der Adresse. Abholen kann nur der Browser, der die Anmeldung begonnen hat — der PKCE-Verifier liegt nur dort. Der alte Tab zeigt dann „Anmeldung im neuen Tab fortgesetzt“.

Öffnet die App einen anderen Browser als den, in dem der Besucher begonnen hat (etwa den eingebauten Browser einer anderen App), fehlt dort der Verifier; die Anmeldung muss dann neu beginnen.

2. Einbindung

Der Baustein ist eine einzelne Datei ohne weitere Abhängigkeiten. Jede Fassung liegt unter einem eigenen Pfad, der sich nie mehr ändert; deshalb lässt sie sich mit einem Integritäts-Hash (SRI) einbinden. Aktuell eingebunden ist Fassung 1.0.1:

<script type="module"
        src="https://mywall3t.integ.kurz.digital/sdk/branch-login/1.0.1/branch-login.js"
        integrity="sha384-DrZfxLuJC+19KfkOsecdJrpO7FXr7sMiXGyGgyGYsZGmJbEJEGo/HKZYx6j3VYsq"
        crossorigin="anonymous"></script>

<mywall3t-branch-login branch="MODELE"
        return-url="https://restaurant-modele.zeitgeist-projekte.de/mitglieder/"></mywall3t-branch-login>

Die Hashes aller Fassungen stehen unter https://mywall3t.integ.kurz.digital/sdk/branch-login/integrity.json.

TeilBedeutung
branchKürzel des Anbieters, Pflicht
return-url registrierte Rücksprung-Adresse, exakt wie registriert; ohne sie gibt es nur den QR-Code
langde oder en; sonst Sprache der Seite
Ereignis mywall3t-login detail ist der Datensatz (Abschnitt 3); kommt auch nach dem Neuladen der Seite
Ereignis mywall3t-logoutder Besucher hat sich abgemeldet
Ereignis mywall3t-errordetail.reason nennt den Grund
me()frischer Datensatz oder null
logout()abmelden; sperrt das Token am Server
accessToken()das Token, für ein eigenes Backend (Abschnitt 4)
Gestaltung CSS-Variablen wie --mywall3t-accent, --mywall3t-radius, --mywall3t-qr-size und ::part(button), ::part(qr), ::part(status), ::part(logout)

Das Token liegt im sessionStorage des Tabs und gilt höchstens 24 Stunden. Eine Seite mit Web-Login sollte keine fremden Skripte laden und eine strenge Content-Security-Policy setzen; diese Demo sendet:

Content-Security-Policy: default-src 'self'; script-src 'self' https://mywall3t.integ.kurz.digital;
  connect-src https://mywall3t.integ.kurz.digital; img-src 'self' https://mywall3t.integ.kurz.digital data:;
  style-src 'self' 'sha256-4WurSkmL96WZW6HbRbHvtussU/sd7IL578afDy6Pu3o='; frame-ancestors 'none'; base-uri 'none';
  form-action 'none'; object-src 'none'
Referrer-Policy: strict-origin-when-cross-origin

Der Baustein bringt seine Gestaltung als <style> im eigenen Shadow-DOM mit. Die Policy erlaubt genau diesen Stil über seinen Hash statt mit 'unsafe-inline'; der Hash gehört zur Fassung und ändert sich mit ihr — beim Wechsel der Fassung also SRI-Hash und Stil-Hash gemeinsam tauschen.

3. Datensatz

GET …/me liefert, was zu diesem Anbieter gehört — nichts von anderen Anbietern und keine Datenart, die der Anbieter nicht ohnehin im Admin-Bereich über sein Mitglied sieht.

{
  "branchWalletAddress": "<Kennung des Besuchers bei diesem Anbieter>",
  "branch": { "abbreviation": "MODELE", "name": "Restaurant Modele" },
  "memberships": [
    { "membershipTypeId": 12, "name": "Stammgast", "symbol": "STG", "serial": 17,
      "imageUrl": "…", "createdAt": "2026-11-10T09:12:00Z" }
  ],
  "coins": [
    { "coinId": 3, "name": "Modele-Punkte", "imageUrl": "…", "balance": 4250,
      "nextExpiry": { "date": "2027-12-31", "amount": 1000 } }
  ]
}

Das Token selbst trägt keine Daten; der Datensatz ist bei jedem Abruf frisch.

4. Token und JWKS

Das Token ist ein JWT, signiert mit Ed25519:

Kopf:   {"alg":"EdDSA","typ":"JWT","kid":"<Schlüssel-Id>"}
Inhalt: {"iss":"https://mywall3t.integ.kurz.digital/api",
         "sub":"<branchWalletAddress>",
         "aud":"branch:MODELE",
         "jti":"<uuid>", "iat":<Sekunden>, "exp":<iat + 24 h>}

Die öffentlichen Schlüssel liegen unter https://mywall3t.integ.kurz.digital/api/branch-login/v1/jwks.json (für eine Stunde zwischenspeicherbar). Ein eigenes Backend prüft das Token damit offline mit jeder JWT-Bibliothek:

Offline sieht ein Backend nicht, ob sich der Besucher inzwischen abgemeldet hat — die Sperre kennt nur der Server. Wer das braucht, ruft mit dem Token selbst GET …/me auf: 200 heißt gültig, 401 heißt abgemeldet, abgelaufen oder ungültig.

Das Token gilt nur für …/me und …/logout genau dieses Anbieters. Es gehört nie in eine URL und nie in ein Log.

5. Registrierung beantragen

Bevor eine Webseite den Web-Login nutzen kann, muss MY WALL3T sie für den Anbieter registrieren. Ohne aktive Registrierung antwortet der Server auf alle Aufrufe mit 404. Eintragen, ändern und abschalten kann nur das MY-WALL3T-Team; Anbieter sehen ihre Registrierung im Admin-Bereich unter „Web-Login“ im Anbieter-Profil. Für den Antrag braucht das Team:

Eine falsch eingetragene Origin gäbe fremdem Code Zugriff auf Mitgliederdaten — deshalb entscheidet darüber MY WALL3T, nicht der Anbieter selbst.

6. Grenzen eines statischen Mitgliederbereichs

Ein Mitgliederbereich auf einer statischen Seite ist nur im Browser „geschützt“. Diese Demo blendet Inhalte erst nach der Anmeldung ein — im Quelltext stehen sie trotzdem. Wer ihn liest, sieht alles, auch ohne Anmeldung und ohne Mitgliedschaft.

Echte Mitglieder-Inhalte brauchen ein Backend: Die Webseite schickt das Token (accessToken()) an den eigenen Server, und erst der liefert die Inhalte aus, nachdem er das Token per JWKS geprüft oder selbst GET …/me aufgerufen hat.

Weitere Grenzen der ersten Fassung: