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.
| Teil | Bedeutung |
|---|---|
branch | Kürzel des Anbieters, Pflicht |
return-url |
registrierte Rücksprung-Adresse, exakt wie registriert; ohne sie gibt es nur den QR-Code |
lang | de oder en; sonst Sprache der Seite |
Ereignis mywall3t-login |
detail ist der Datensatz (Abschnitt 3); kommt auch nach dem Neuladen der Seite |
Ereignis mywall3t-logout | der Besucher hat sich abgemeldet |
Ereignis mywall3t-error | detail.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 } }
]
}
-
branchWalletAddressist eine Kennung nur für diesen Anbieter — dieselbe, die der Anbieter im Admin-Bereich sieht. Sie gibt es auch für Besucher ohne Mitgliedschaft. membershipsist leer, wenn der Besucher kein Mitglied ist.serialist die Mitgliedsnummer; ist keine vergeben, fehlt das Feld.-
coinsenthält jede Punkte-Art, die bei diesem Anbieter gilt, auch gemeinsam mit anderen Anbietern genutzte.balancesind ganze Punkte. - Gutscheine und Tickets sind nicht enthalten.
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:
- Signatur gegen den Schlüssel mit passender
kid(AlgorithmusEdDSA), isswie oben,audgenaubranch:<eigenes Kürzel>,expnicht überschritten.
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:
- das Kürzel des Anbieters,
-
die Origins der Webseite, genau
https://hostoderhttps://host:port— ohne Pfad, ohne Platzhalter;httpnur fürhttp://localhost:<port>zum Entwickeln, - die Rücksprung-Adressen als vollständige URLs; sie werden exakt verglichen und müssen zu einer der Origins gehören. Ohne Rücksprung-Adresse gibt es nur den QR-Code.
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:
- nur lesen — Kaufen, Einlösen oder Gutschreiben über die Webseite gibt es nicht,
- keine Verlängerung: nach 24 Stunden oder in einem neuen Tab meldet sich der Besucher neu an,
- eine Webseite gehört zu genau einem Anbieter,
- Kaufangebote pflegt die Webseite selbst; diese Demo zeigt die Mitgliedschaft „Stammgast“ mit einem Beispiel-Link.