Sicherheitsgrundlagen
Supabase "Auth session missing!": wo die Sitzung geblieben ist
"Auth session missing!" heißt: supabase-js fand keine angemeldete Sitzung und fragte Supabase gar nicht erst. Wo die Sitzung blieb, und die Lösung je Fall.

Kurz gesagt
- "Auth session missing!" (AuthSessionMissingError, Status 400) kommt aus supabase-js in deiner App. Die Bibliothek suchte eine angemeldete Sitzung, fand keine und schickte die Anfrage nie an Supabase.
- Auf einer Seite, auf der sich niemand angemeldet hat, ist das die richtige Antwort, und dein Code muss sie nur behandeln. Auf einer Seite zum Zurücksetzen des Passworts heißt es, dass der Link hier niemanden angemeldet hat. In einer Edge Function oder auf einem Server wurde die Sitzung des Besuchers nie weitergereicht.
- Lass getUser() stehen. Wer auf einem Server stattdessen getSession() einsetzt, lässt den Fehler verschwinden, indem er glaubt, was immer im Cookie steht.
Deine Lovable- oder Bolt-App meldet Leute mit Supabase an. Eines Morgens öffnest du die Browserkonsole und findest das hier in Rot, auf einer Seite, an der nichts falsch aussieht:
AuthSessionMissingError: Auth session missing!
Oder eine Kundin schreibt dir, dass die Seite "Neues Passwort festlegen" sie mit genau diesen Worten abweist, jedes Mal.
Such nach "auth session missing supabase", und du findest Threads voller
Lösungen: getUser() gegen getSession() tauschen, die Bibliothek
herunterstufen, eine Wartezeit einbauen, das Projekt neu starten. Sie gehen
davon aus, dass zwischen deiner App und Supabase etwas kaputtgegangen ist. Es
wurde keine Anfrage gestellt. Der Fehler entsteht in deiner App, geschrieben
von der Supabase-Bibliothek, bevor irgendetwas den Browser verlässt.
Was bedeutet "Auth session missing!" in Supabase?
supabase-js wurde gefragt, wer angemeldet ist, suchte eine Sitzung, fand keine und antwortete selbst.
Eine Sitzung ist das, was Supabase Auth einer Person bei der Anmeldung gibt: ein Paar Tokens, die sagen, wer sie ist, im Browser aufbewahrt, damit auch der nächste Seitenaufruf sie noch kennt. supabase-js ist die Bibliothek, mit der deine App mit Supabase spricht, und jeder Aufruf, der eine Person braucht, beginnt damit, diese Sitzung hervorzuholen.
Die Meldung steht in supabase-js selbst, in der Datei errors.ts der
Auth-Bibliothek: ein AuthSessionMissingError mit dem Text
Auth session missing! und dem Status 400. Die 400 ist ein Etikett, das sich
die Bibliothek selbst gibt. Kein Server hat damit geantwortet, und die Logs von
Supabase zeigen nichts, weil getUser() vor allem anderen nach einer Sitzung
sieht. Ein Kommentar in seinem Quellcode sagt es so: Es gibt einen Fehler
zurück, wenn es weder ein Zugriffstoken noch einen eigenen
Authorization-Header gibt.
Stell dir einen Empfang vor, der dir sagen kann, welcher Besucher jemand ist. Wer sich anmeldet, bekommt an der Tür einen Ausweis, und supabase-js bewahrt diesen Ausweis in der Tasche des Browsers auf. Wenn dein Code fragt "Wer ist das?", greift supabase-js zuerst in die Tasche. Ist ein Ausweis darin, trägt es ihn zum Empfang und bringt einen Namen zurück. Ist die Tasche leer, geht es gar nicht erst zum Empfang. Es kommt sofort zurück und sagt dir, dass kein Ausweis da ist.
Er wird zurückgegeben, nicht geworfen
getUser() gibt den Fehler neben einem leeren Nutzer zurück, und dein Code
entscheidet, was als Nächstes passiert.
Der Aufruf sieht so aus, und ohne Sitzung ist user danach null und error
der AuthSessionMissingError:
const { data: { user }, error } = await supabase.auth.getUser()
Es wird nichts geworfen, also fängt ein try … catch um den Aufruf nichts. Von
dort kommt der sichtbare Schaden, in einer von zwei Formen:
- Der Code zeigt jeden
erroran, den er bekommt. Ein Besucher, der sich noch nicht angemeldet hat, sieht "Auth session missing!" in einer Meldung oder einem roten Kasten, auf einer Seite, die er sehen durfte. - Der Code benutzt den Nutzer, ohne nachzusehen. Er liest
user.id, und die Seite bricht mitCannot read properties of nullab, was nach einem ganz anderen Fehler aussieht.
Beides ist derselbe Fehler: "Niemand ist angemeldet" wird als Störung behandelt. Sieh zuerst nach dem Nutzer und zeig einem abgemeldeten Besucher die Seite, die für ihn gedacht ist:
const { data: { user } } = await supabase.auth.getUser()
if (!user) {
// niemand ist angemeldet: Anmeldeseite oder öffentliche Seite zeigen
return
}
Wenn du das von einer echten Störung unterscheiden willst, nennt die Bibliothek
den Fall beim Namen: error.name ist 'AuthSessionMissingError'.
Wo die Sitzung verloren geht
An drei Stellen, und nur die erste ist harmlos.
1. In diesem Browser hat sich niemand angemeldet. Deine App ruft getUser()
auf einer Seite auf, die jeder öffnen kann, etwa um zu entscheiden, ob
"Anmelden" oder "Mein Konto" erscheint. Für einen Besucher ist die leere Tasche
die zutreffende Antwort. Hier begegnen die meisten der Meldung, und die
Code-Lösung oben ist alles, was es braucht.
2. Eine Seite für das Zurücksetzen des Passworts oder einen Magic Link. Ein
neues Passwort setzt updateUser({ password }), und der
Passwort-Leitfaden von Supabase
ist bei der Reihenfolge eindeutig: Erst wenn eine Sitzung besteht, sammelt man
das neue Passwort ein und ruft updateUser auf. Auf dieser Seite soll die
Sitzung aus dem Link in der E-Mail kommen, der die Person beim Öffnen anmeldet.
Tut er das nicht, ist die Tasche leer, und updateUser() antwortet mit "Auth
session missing!". Die üblichen Gründe:
- Der Link war schon benutzt oder abgelaufen, bevor er geöffnet wurde. Warum Supabase sagt, der E-Mail-Link sei ungültig oder abgelaufen geht jeden Grund durch.
- Er wurde in einem anderen Browser oder einer anderen App geöffnet als dem, der ihn angefordert hat. Nutzt eine App die PKCE-Anmeldung von Supabase, bleibt ein Teil des Austauschs in dem Browser, der ihn begonnen hat, und laut der Dokumentation von Supabase muss der Austausch des Codes im selben Browser und auf demselben Gerät angestoßen werden, auf dem der Ablauf begann.
- Er schickte die Person woandershin, etwa nach
localhost, sodass die Sitzung an einer Adresse landete, die nicht deine App ist. Warum Supabase auf localhost umleitet behandelt diesen Fall.
3. Dein Server oder eine Edge Function hat die Sitzung nie bekommen. Der
Ausweis liegt im Browser deines Besuchers. Code, der woanders läuft, eine
Supabase Edge Function oder die Serverhälfte einer Next.js-App aus v0 oder
Cursor, erzeugt seinen eigenen Supabase-Client, und ein neuer Client beginnt mit
leerer Tasche. Solange die Sitzung des Besuchers nicht hineingereicht wird,
findet getUser() dort nichts und antwortet mit "Auth session missing!", ohne
Supabase zu fragen.
In einer Edge Function reist die Sitzung im Header Authorization der Anfrage
mit, und supabase-js nutzt sie nur, wenn du sie beim Erzeugen des Clients
übergibst. In einer Next.js-App reist sie in Cookies, und der Server-Helfer von
Supabase liest sie nur, wenn du ihm die Cookies der Anfrage gibst. Reicht eine
Edge Function diesen Fehler als Fehler zurück, zeigt deine App
"Edge Function returned a non-2xx status code",
und im Log der Funktion wartet diese Meldung.
Woran du erkennst, welcher Fall es ist
Sieh, wo die Meldung erscheint, und dann, was der Browser tatsächlich geschickt hat.
In der Browserkonsole, auf einer Seite, die jeder öffnen kann. Fall 1. Öffne
die Entwicklerwerkzeuge deines Browsers (F12), wähle Application, dann
Local Storage und die Adresse deiner App. supabase-js bewahrt die Sitzung
unter einem Schlüssel auf, der aus sb-, der ID deines Projekts und
-auth-token besteht. Gibt es keinen solchen Eintrag, hat dieser Browser keine
Sitzung, und die Meldung sagt die Wahrheit. Eine Next.js-App mit den
Server-Helfern von Supabase bewahrt die Sitzung stattdessen in Cookies auf, also
sieh unter Cookies nach demselben Namen.
Auf einer Seite zum Zurücksetzen oder für einen Magic Link. Fall 2. Sieh dir die Adresse an, unter der sich der Link geöffnet hat. Ist es nicht deine App, oder ist es ein Link, den du oder ein Mail-Scanner schon geöffnet hatte, hatte die Sitzung nie eine Chance anzukommen.
Im Log einer Edge Function oder deines Servers. Fall 3. Öffne den Tab
Network, wiederhole die Aktion und klick auf die Anfrage an deine Funktion.
Enthält ihr Header Authorization eine lange Zeichenkette, die mit eyJ
beginnt, hat der Browser das Token einer Person geschickt, und deine Funktion
hat es nicht an ihren Client weitergegeben.
Was du auf keinem Weg findest, ist eine Anfrage an /auth/v1/user, die mit
dieser Meldung zurückkam. Siehst du doch eine, die fehlgeschlagen ist, ist es
ein anderer Fehler. Eine Antwort mit invalid claim: missing sub claim heißt,
dass der Schlüssel deines Projekts dorthin geschickt wurde, wo eine Person
hingehört, und dieser Beitrag ist sein
Zwilling.
Die Lösung für jeden Fall
Gib jedem Fall die Sitzung, die ihm fehlt, oder eine Antwort, die keine braucht.
Fall 1, der abgemeldete Besucher: Sieh nach user, bevor du ihn benutzt,
wie im Code oben, und zeig die Seite, die für jemanden gedacht ist, der sich
nicht angemeldet hat.
Fall 2, die Seite zum Zurücksetzen: Warte, bis der Link die Person
angemeldet hat, bevor du das Formular zeigst. Supabase sendet das Ereignis
PASSWORD_RECOVERY, wenn jemand über einen Wiederherstellungslink ankommt, und
seine Referenz zeigt die Seite, wie sie darauf wartet:
supabase.auth.onAuthStateChange((event) => {
if (event === 'PASSWORD_RECOVERY') {
// der Link hat die Person angemeldet: jetzt das Formular für das neue Passwort zeigen
}
})
Kommt das Ereignis nie, hat der Link nicht funktioniert. Sag das und biete an, einen neuen zu schicken, statt ein Formular zu zeigen, das sicher scheitern wird.
Fall 3, die Funktion oder der Server: Gib die Sitzung des Besuchers an den Client weiter, den du erzeugst. In einer Edge Function erzeugst du ihn mit dem eingehenden Header:
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_ANON_KEY')!,
{ global: { headers: { Authorization: req.headers.get('Authorization')! } } },
)
const { data: { user } } = await supabase.auth.getUser()
if (!user) {
return new Response(JSON.stringify({ error: 'Sign in first' }), {
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
})
}
Der neuere Wrapper withSupabase({ auth: 'user' }) von Supabase erledigt
dasselbe, bevor dein Handler läuft, und der
Beitrag zur Edge Function zeigt die
Einrichtung. Behalte die 401 für einen Aufrufer ohne Sitzung, damit eine Anfrage
von einer abgemeldeten Seite eine klare Ablehnung bekommt. Mit weitergereichtem
Header kann getUser() für einen abgemeldeten Aufrufer dort stattdessen
invalid claim: missing sub claim zurückgeben: derselbe Besucher, von der
anderen Seite gesehen, und die 401 deckt beides ab.
In einer Next.js-App erzeugst du den Server-Client mit dem Helfer
@supabase/ssr von Supabase und den Cookies der Anfrage, wie es seine
Server-Einrichtung zeigt, und rufst getUser() auf diesem Client auf.
Zwei Lösungen, nach denen du schlechter dastehst
Beide werden für diese Meldung vorgeschlagen, und jede stoppt den Fehler, indem sie die Prüfung dahinter entfernt.
getUser() gegen getSession() tauschen. Im Browser ändert das nichts, was
zählt: Der Speicher ist immer noch leer, also bekommst du keine Sitzung und
keinen Nutzer, nur ohne die Meldung. Auf einem Server ist es gefährlich. Der
Leitfaden von Supabase für die Serverseite sagt, man solle
supabase.auth.getSession() in Servercode nie trauen, weil es die Sitzung aus
dem Cookie liest, ohne sie neu zu prüfen. Ein Cookie ist etwas, das dir der
Besucher schickt, und jeder kann dir jedes Cookie schicken, das er will.
getUser() ist die Methode, von der supabase-js dokumentiert, dass sie eine
Anfrage an den Server von Supabase Auth stellt, sodass ihr Ergebnis echt ist.
Behalte sie überall, wo die Antwort entscheidet, was jemand darf.
Den geheimen Schlüssel in den Client setzen, damit der Aufruf nicht mehr
scheitert. Ein Client mit dem Schlüssel service_role oder sb_secret_
braucht keine Sitzung zum Lesen und Schreiben, weil er die Regeln deiner
Datenbank ganz übergeht. Die Funktion läuft wieder, für jeden Aufrufer,
angemeldet oder nicht. Macht ein Builder diese Änderung in Code, der an den
Browser geht, reist der Schlüssel mit zu jedem Besucher. Ist das schon passiert,
rotiere ihn zuerst.
Das lässt sich von außen prüfen. Unser kostenloser Scan liest das JavaScript deiner Live-App so, wie ein Fremder es täte, und nennt jeden Schlüssel, den er findet, samt der Frage, ob er dorthin gehört. Das dauert etwa 20 Sekunden und braucht kein Konto: scanne deine App.
Damit die Prüfung nach der nächsten Änderung bleibt
Die Prüfung, wer angemeldet ist, ist der Teil einer Seite, der am ehesten verschwindet, wenn du deinen Builder bittest, einen Fehler wegzumachen, und beide schnellen Lösungen oben entfernen sie.
Reeve Monitor scannt deine Live-App jede Stunde neu:
- alle neun Checks jede Stunde, für bis zu drei Apps
- eine E-Mail an dem Tag, an dem ein erneuter Scan deine App schlechter benotet als der vorige; ein geheimer Schlüssel in deinem JavaScript deckelt die Note allein schon bei D
- ob die App erreichbar ist, alle 60 Sekunden
- ein monatlicher Bericht darüber, was gesehen wurde
Ein Server, der jedem Cookie glaubt, oder eine Funktion, die mit dem geheimen Schlüssel läuft, lässt einen Fremden die Zeilen deiner Nutzer ändern. Reeve Care hält eine Kopie deiner Supabase-Datenbank von davor.
- eine verschlüsselte Kopie nach Zeitplan, auf Reeves eigenem Speicher in der EU, außerhalb deines Supabase-Kontos
- jede Kopie wird geprüft, bevor sie zählt, durch Zählen der Zeilen in jeder Tabelle
- eine Wiederherstellung per Klick, die sichert, was jetzt da ist, bevor sie es ersetzt
- deine hochgeladenen Dateien ebenfalls, sobald du eine Storage-Zugangsberechtigung verbindest
- alles, was Monitor tut
Was jetzt zu tun ist
Was zu tun ist
- Lies "Auth session missing!" als "hier ist niemand angemeldet". Die Anfrage hat deine App nie verlassen.
- Sieh nach, ob
usergleichnullist, bevor du ihn benutzt, und zeig einem abgemeldeten Besucher die Seite, die für ihn gedacht ist, statt des Fehlers. - Warte auf einer Seite zum Zurücksetzen auf das Ereignis
PASSWORD_RECOVERY, bevor du das Formular zeigst, und biete einen neuen Link an, wenn es nie ankommt. - Gib in einer Edge Function oder auf einem Server den Header
Authorizationoder die Cookies des Besuchers an den Client weiter, den du erzeugst, und antworte mit 401, wenn es trotzdem keinen Nutzer gibt. - Behalte
getUser()auf dem Server.getSession()glaubt dort jedem Cookie, das es geschickt bekommt.
Gibt es deine Sitzungen und verschwinden sie dann, sodass Leute, die angemeldet waren, plötzlich abgemeldet sind, ist das ein anderes Problem mit eigenen Ursachen, und warum Supabase-Nutzer abgemeldet werden behandelt es. Die 10-Minuten-Sicherheitscheckliste behandelt, was eine frisch gestartete App sonst noch oft offen lässt.
FAQ
Was bedeutet "Auth session missing!" in Supabase?
Es bedeutet, dass supabase-js, die Supabase-Bibliothek in deiner App, nach dem angemeldeten Nutzer gefragt wurde und keine Sitzung hatte, mit der sie fragen konnte. Eine Sitzung ist das, was Supabase Auth einer Person bei der Anmeldung gibt, und die Bibliothek bewahrt sie im Browser auf. Ist keine da, wendet sie sich gar nicht an Supabase. Sie gibt AuthSessionMissingError mit dem Status 400 zurück und als Nutzer null.
Warum sehe ich "Auth session missing!", wenn niemand angemeldet ist?
Weil das die Antwort für einen Besucher ist, der sich nicht angemeldet hat. Deine App hat getUser() auf einer Seite aufgerufen, die jeder öffnen kann, und supabase-js hat gemeldet, dass niemand angemeldet war. Nichts ist kaputt. Dein Code muss den Nutzer nur als null lesen und die Anmeldeseite zeigen, statt den Fehler anzuzeigen.
Warum zeigt meine Seite zum Zurücksetzen des Passworts "Auth session missing!"?
Ein Passwort mit updateUser() zu ändern braucht eine Sitzung, und auf dieser Seite soll die Sitzung aus dem Link in der E-Mail kommen. War der Link schon benutzt oder abgelaufen, wurde er in einem anderen Browser geöffnet als dem, der ihn angefordert hat, oder schickte er die Person an eine andere Adresse, dann kam keine Sitzung an, und updateUser() hat niemanden, den es ändern könnte. Warte auf das Ereignis PASSWORD_RECOVERY, bevor du das Formular zeigst, und biete einen neuen Link an, wenn es nie kommt.
Ist "Auth session missing!" dasselbe wie "invalid claim: missing sub claim"?
Nein. "Auth session missing!" wird in deiner App entschieden, bevor irgendeine Anfrage sie verlässt. "invalid claim: missing sub claim" kommt von Supabase Auth zurück, das eine Anfrage bekommen und dort, wo das Token einer Person hingehört, den Schlüssel deines Projekts gefunden hat. Das Erste heißt, es gab nichts zu schicken. Das Zweite heißt, es wurde das Falsche geschickt.
Soll ich getSession() statt getUser() verwenden?
Nicht, um diesen Fehler loszuwerden. Im Browser findet getSession() denselben leeren Speicher und gibt keine Sitzung zurück, also ändert sich nichts, außer dass die Fehlermeldung verschwindet. Auf einem Server ist es schlimmer: supabase-js dokumentiert, dass getSession() die Sitzung direkt aus dem Speicher liest, etwa aus Cookies der Anfrage, die nicht echt sein müssen, und empfiehlt stattdessen getUser(), weil es bei Supabase Auth nachfragt.