{ }
type
GraphQL · Authentifizierung · JWT · Field-Level-Auth · Magento
GraphQL Authentifizierung:
Tokens, Sessions und Field-Level-Auth

Authentifizierung in GraphQL ist kein Sonderfall – sie ist Bestandteil jedes Resolvers, der auf nutzerspezifische Daten zugreift. Wer Token-Validierung, Session-Kontext und feldgenaue Zugriffskontrolle nicht explizit modelliert, baut Sicherheitslücken in das Schema ein, bevor die erste Query in Produktion geht.

15 Min. Lesezeit JWT · Bearer · Context-Objekt · Field-Guard · Magento-Auth GraphQL · Magento 2 · PHP

1. Warum Authentifizierung in GraphQL anders funktioniert als in REST

In REST-APIs ist Authentifizierung meist auf Routen-Ebene geregelt: Ein Middleware-Layer prüft den Bearer-Token, bevor der Controller-Code läuft. In GraphQL existiert typischerweise nur ein einziger Endpunkt – /graphql – der alle Operationen entgegennimmt. Das bedeutet: Die Entscheidung, ob eine Anfrage berechtigt ist, kann nicht mehr pauschal auf HTTP-Ebene getroffen werden, sondern muss im Resolver selbst oder in einem Middleware-Layer vor dem Execution-Engine stattfinden.

Diese Verschiebung hat direkte Konsequenzen für das Schema-Design und die Resolver-Architektur. Felder, die nur für eingeloggte Nutzer sinnvoll sind, müssen im Resolver explizit geprüft werden – ein fehlendes Guard führt zur stillen Datenoffenbarung, weil GraphQL keinen eingebauten Auth-Mechanismus auf Feldebene kennt. Die Folge: Authentifizierung in GraphQL ist keine Infrastrukturentscheidung, sondern ein Designthema, das für jedes Feld und jeden Resolver bewusst getroffen werden muss.

2. Token-Typen: JWT, Bearer und opake Session-Token im Vergleich

Die drei häufigsten Ansätze für die Authentifizierung in GraphQL-APIs sind JWT (JSON Web Tokens), opake Bearer-Tokens und Server-seitige Sessions. JWT-Tokens enthalten alle relevanten Informationen – Nutzer-ID, Rollen, Ablaufzeit – im Token selbst und müssen nicht gegen eine Datenbank validiert werden. Das macht sie zustandslos und gut skalierbar, erfordert aber eine sorgfältige Handhabung von Token-Rotation und -Invalidierung, da ein ausgestelltes JWT bis zu seinem Ablauf gültig bleibt.

Opake Bearer-Tokens sind zufällige Strings, die serverseitig einer Session zugeordnet sind. Jede Validierung erfordert einen Datenbankzugriff oder Cache-Lookup, dafür können sie sofort invalidiert werden. Magento nutzt dieses Modell: Über die Mutation generateCustomerToken wird ein opaker Token ausgestellt, der bei jeder Anfrage im Authorization-Header mitgeschickt und gegen die Datenbank geprüft wird. Server-seitige Sessions via Cookie sind im GraphQL-Kontext seltener, tauchen aber in hybriden Setups auf, wo ein Browser-Frontend und eine GraphQL-API dieselbe Session teilen.


# Token generation — Magento customer login mutation
mutation GenerateCustomerToken($email: String!, $password: String!) {
  generateCustomerToken(email: $email, password: $password) {
    token
  }
}

# Using the token: Authorization header in all subsequent requests
# Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

# Revoke token on logout
mutation RevokeCustomerToken {
  revokeCustomerToken {
    result
  }
}

3. Das Context-Objekt: Authentifizierung einmal validieren, überall nutzen

Das Context-Objekt ist das zentrale Muster für Authentifizierung in GraphQL. Es wird einmal beim Aufbau der Execution-Engine befüllt – typischerweise in einem Middleware-Layer, der den Authorization-Header liest, den Token validiert und die Nutzerinformationen extrahiert – und steht dann jedem Resolver als drittes Argument zur Verfügung. So wird der Token nicht in jedem Resolver einzeln geparst, sondern nur einmal pro Request.

In Magento implementiert der GraphQlContext dieses Muster: Er enthält die getUserId()-Methode, die getUserType() und weitere kontextuelle Informationen. Ein Resolver, der nutzerspezifische Daten liefert, prüft zu Beginn, ob $context->getUserId() einen gültigen Wert zurückgibt, und wirft andernfalls eine GraphQlAuthorizationException. Dieses Muster ist konsistent, testbar und vermeidet doppelte Token-Validierung in jeder Resolver-Methode.

4. Resolver-Guards: Zugriff im Resolver absichern

Ein Resolver-Guard ist eine explizite Prüfung am Anfang jeder Resolver-Methode, die feststellt, ob der aufrufende Nutzer berechtigt ist, dieses Feld zu sehen. Der Guard verwendet das Context-Objekt und wirft eine typisierte Exception, wenn die Berechtigung fehlt. In Magento gibt es dafür dedizierte Exception-Klassen: GraphQlAuthorizationException für fehlende Berechtigung und GraphQlAuthenticationException für fehlende Identität. Die Unterscheidung ist wichtig, weil sie sich im HTTP-Statuscode und im extensions.category-Feld der Fehlerantwort widerspiegelt.

Ein häufiges Anti-Pattern ist der fehlende Guard bei Queries, die nach außen harmlos wirken. Eine Produkt-Query, die keine nutzerabhängigen Daten zurückgibt, braucht keinen Auth-Guard. Aber sobald ein Resolver Kundendaten, Preisgruppen oder personalisierte Inhalte liefert, muss der Guard da sein – unabhängig davon, ob das Frontend den entsprechenden Button zeigt oder nicht. Sicherheit auf GraphQL-Ebene darf sich nie auf die Annahme stützen, dass nur autorisierte Clients die Query schicken.


# Query that requires authentication — must be guarded in resolver
query CustomerProfile {
  customer {
    firstname
    lastname
    email
    addresses {
      street
      city
      postcode
    }
  }
}

# Expected error response when token is missing or invalid
# HTTP 200 (GraphQL always returns 200), error in payload:
# {
#   "errors": [{
#     "message": "Der aktuelle Kunde ist nicht autorisiert.",
#     "extensions": { "category": "graphql-authorization" }
#   }],
#   "data": { "customer": null }
# }

5. Field-Level-Auth: Felder anhand von Rollen und Scopes sperren

Field-Level-Auth geht einen Schritt weiter als Resolver-Guards: Nicht nur der Resolver insgesamt wird abgesichert, sondern einzelne Felder innerhalb eines Typs können für unterschiedliche Rollen unterschiedlich zugänglich sein. Ein klassisches Beispiel ist ein User-Typ, bei dem das Feld email für normale Nutzer sichtbar ist, das Feld internalScore aber nur für Administratoren. Ohne explizite Field-Level-Auth gibt GraphQL alle angefragten Felder zurück, die der Resolver zurückgibt.

In der Praxis wird Field-Level-Auth auf zwei Arten implementiert: Entweder werden sensitive Felder in einem separaten Typ gekapselt, der nur für bestimmte Rollen auflösbar ist, oder der Resolver selbst gibt null für Felder zurück, auf die der Nutzer keinen Zugriff hat. Letzteres ist einfacher zu implementieren, aber weniger explizit. Eine dritte Möglichkeit sind Schema-Directives wie @auth(requires: ADMIN), die vor dem Resolver-Aufruf greifen und bei fehlender Berechtigung automatisch null zurückgeben.

6. Authentifizierung in Magento GraphQL: Token-Mutation und Kontext

Magento implementiert ein vollständiges Token-basiertes Auth-System für GraphQL. Kunden generieren einen Token über generateCustomerToken, Administratoren über generateCustomerTokenAsAdmin. Der Token wird als Bearer-Header in alle nachfolgenden Anfragen eingebettet. Magento validiert den Token bei jeder Anfrage, befüllt den GraphQlContext und stellt die Nutzerinformationen allen Resolvern zur Verfügung.

Ein wichtiger Aspekt im Magento-Kontext ist das Gast-vs.-Kunden-Szenario: Viele Magento-GraphQL-Endpunkte erlauben sowohl authentifizierte als auch nicht-authentifizierte Anfragen, liefern aber unterschiedliche Daten zurück. Der cart-Resolver beispielsweise unterscheidet anhand der maskedCartId (Gast) vs. kundenbezogenen Cart-ID, ob ein eingeloggter Nutzer oder ein Gast seine Warenkorb-Daten abruft. Resolver müssen diesen Unterschied explizit behandeln und dürfen nicht davon ausgehen, dass alle Aufrufer authentifiziert sind.

7. Fehlerformat bei Auth-Fehlern: extensions.category richtig setzen

GraphQL-Fehler bei fehlgeschlagener Authentifizierung sollten strukturierte Informationen im extensions-Feld der Fehlerantwort enthalten. Magento setzt dabei auf die category-Property im extensions-Objekt: graphql-authorization für fehlende Berechtigungen und graphql-authentication für fehlende Identität. Diese Kategorisierung erlaubt es dem Frontend, Auth-Fehler programmatisch zu erkennen und entsprechend zu reagieren – etwa durch Weiterleitung zum Login oder durch Anzeige einer Fehlermeldung.

Ein häufiger Fehler ist, Auth-Probleme als generische Fehler zurückzugeben, die keine Information über die Art des Problems enthalten. Das erschwert die Fehlerdiagnose und zwingt das Frontend zu Heuristiken. Das korrekte Muster: typisierte Exceptions werfen, die das Framework in strukturierte extensions-Daten überführt. Gleichzeitig gilt: Keine internen Stacktraces oder Systeminformationen in die Fehlerantwort aufnehmen – nur die Information, die der Client für eine sinnvolle Reaktion braucht.


# Structured auth error response — correct format with extensions.category
{
  "errors": [
    {
      "message": "Der aktuelle Kunde ist nicht autorisiert, diese Ressource abzurufen.",
      "locations": [{ "line": 2, "column": 3 }],
      "path": ["customer"],
      "extensions": {
        "category": "graphql-authorization"
      }
    }
  ],
  "data": {
    "customer": null
  }
}

# Frontend can detect auth errors by checking extensions.category:
# if (error.extensions?.category === 'graphql-authorization') { redirectToLogin() }

8. Auth-Ansätze im direkten Vergleich

Die Wahl des Authentifizierungsansatzes hat direkte Auswirkungen auf Skalierbarkeit, Sicherheit und Implementierungsaufwand. Keiner der Ansätze ist universell überlegen – die richtige Wahl hängt vom konkreten Anwendungsfall ab.

Ansatz Vorteil Nachteil Einsatzgebiet
JWT (stateless) Kein DB-Lookup, horizontal skalierbar Kein Sofort-Revoke möglich Headless-APIs, Microservices
Opaker Bearer Sofortiger Revoke via DB DB-Lookup bei jedem Request Magento, klassische Shops
Session-Cookie Browser-nativ, einfach CSRF-Schutz erforderlich Server-rendered + GraphQL hybrid
Field-Directive Deklarativ, gut lesbar Framework-Abhängigkeit Node.js, Apollo Server
Resolver-Guard Explizit, framework-unabhängig Repetitiv, Vergessen möglich Magento, PHP-APIs

Im Magento-Kontext ist der Resolver-Guard mit opakem Bearer-Token der Standard. Jede Abweichung – etwa ein JWT-basiertes Auth-System für ein externes Frontend – erfordert, dass der Token-Validierungs-Layer im GraphQlContext entsprechend angepasst wird. Wer JWT extern validiert und das Ergebnis in den Magento-Kontext injiziert, kann das bestehende Guard-Muster nutzen, ohne die Resolver zu verändern.

9. Typische Authentifizierungsfehler und wie man sie erkennt

Der häufigste Fehler bei GraphQL-Authentifizierung ist das Fehlen eines Guards in Resolvern, die implizit als nicht öffentlich gelten, aber nie explizit abgesichert wurden. Dieser Fehler taucht oft bei neu hinzugefügten Feldern auf, weil der Entwickler vergisst, den Guard aus dem übergeordneten Resolver zu replizieren. Eine Konvention helfen kann: jeder Resolver, der auf Kundendaten, Preisgruppen oder personalisierte Inhalte zugreift, muss einen Guard als erstes Statement haben, bevor andere Logik ausgeführt wird.

Ein zweiter verbreiteter Fehler ist die Verwendung desselben Token-Types für unterschiedliche Nutzerklassen ohne klare Trennung. Wenn ein Gast-Token und ein Kunden-Token strukturell identisch sind, ist es leicht, einen Resolver zu schreiben, der einen Gast-Token als Kunden-Token akzeptiert. Das korrekte Muster: getUserType() explizit prüfen und verschiedene Nutzerklassen in dedizierte Pfade routen. Magento unterscheidet hier zwischen UserContextInterface::USER_TYPE_CUSTOMER und USER_TYPE_GUEST.


# WRONG — no guard, exposes customer data to unauthenticated requests
# resolver returns customer data without checking context.getUserId()

# RIGHT — explicit guard as first statement in resolver
# PHP resolver pattern (Magento):
#
# public function resolve(Field $field, $context, ResolveInfo $info, ...): array
# {
#     // Guard: check authentication before any business logic
#     if (false === $context->getExtensionAttributes()->getIsCustomer()) {
#         throw new GraphQlAuthorizationException(
#             __('Der aktuelle Kunde ist nicht autorisiert.')
#         );
#     }
#     // Business logic only reached if authenticated
#     return $this->customerDataProvider->get($context->getUserId());
# }

# WRONG — same error response for missing token and wrong role
# { "errors": [{ "message": "Fehler" }] }

# RIGHT — differentiated error with extensions.category
# { "errors": [{ "message": "...", "extensions": { "category": "graphql-authorization" } }] }

10. Zusammenfassung

Authentifizierung in GraphQL erfordert ein anderes Denken als in REST-APIs: Kein Route-Level-Guard reicht aus, weil alle Operationen über denselben Endpunkt laufen. Das Context-Objekt löst das Problem, indem Token-Validierung einmal pro Request stattfindet und das Ergebnis allen Resolvern zur Verfügung steht. Resolver-Guards am Anfang jeder relevanten Methode sorgen dafür, dass nutzerspezifische Daten nie ohne explizite Prüfung ausgeliefert werden. Field-Level-Auth erweitert dieses Muster auf Felder innerhalb eines Typs und ermöglicht rollenbasierte Sichtbarkeit ohne Schema-Fragmentierung.

Magento liefert mit generateCustomerToken, GraphQlContext und den typisierten Auth-Exceptions ein vollständiges Fundament, das konsequent genutzt werden sollte. Typische Fehler – fehlende Guards bei neu hinzugefügten Feldern, fehlende Unterscheidung zwischen Gast und Kunde, generische statt kategorisierte Fehlerantworten – entstehen meist dann, wenn Auth als nachträgliche Ergänzung behandelt wird statt als Teil des Resolver-Designs von Anfang an.

GraphQL Authentifizierung — Das Wichtigste auf einen Blick

Context-Objekt

Token einmal validieren, Ergebnis im Context-Objekt allen Resolvern zur Verfügung stellen. Kein Token-Parsing in jedem Resolver.

Resolver-Guard

Explizite Auth-Prüfung als erstes Statement in jedem Resolver, der nutzerspezifische Daten liefert. Niemals implizit davon ausgehen, dass ein Aufrufer autorisiert ist.

Field-Level-Auth

Sensitive Felder in separaten Typen kapseln oder null zurückgeben, wenn die Rolle nicht ausreicht. Schema-Directives sind deklarativer, aber framework-abhängig.

Fehlerformat

extensions.category: graphql-authorization für fehlende Rolle, graphql-authentication für fehlende Identität. Kein interner Stacktrace in der Antwort.

11. FAQ: GraphQL Authentifizierung, Tokens, Sessions und Field-Level-Auth

1Kann Authentifizierung auf HTTP-Middleware-Ebene abgehandelt werden?
Teilweise. Token-Validierung kann in Middleware erfolgen, die feldgenaue Zugriffsentscheidung muss aber im Resolver getroffen werden.
2Unterschied graphql-authorization vs. graphql-authentication?
authentication: kein gültiger Token vorhanden. authorization: Token vorhanden, aber Rolle oder Scope reicht nicht aus.
3Wie funktioniert Token-Validierung in Magento GraphQL?
Authorization-Header wird pro Request geprüft, Token gegen oauth_token-Tabelle validiert, Kunden-ID in den GraphQlContext geladen.
4Field-Level-Auth in Magento umsetzen?
Rolle im Resolver prüfen, für nicht berechtigte Felder null zurückgeben oder Exception werfen. Keine eingebauten Schema-Directives in Magento.
5Kann ein Resolver null zurückgeben statt Fehler zu werfen?
Ja, bei optionalen Feldern. Relevant ist, ob das Frontend zwischen fehlenden Daten und fehlender Berechtigung unterscheiden muss.
6Warum gibt Magento bei Auth-Fehlern HTTP 200 zurück?
GraphQL-Standard: immer HTTP 200, Fehlerinformationen stehen im errors-Array der JSON-Antwort.
7Wie invalidiert man einen Magento-Token sofort?
Über revokeCustomerToken-Mutation. Opake Tokens können sofort invalidiert werden, JWTs nicht.
8Was ist ein Gast-Token in Magento GraphQL?
Kein echter Token, sondern eine maskedCartId für anonyme Warenkörbe. Gäste werden als USER_TYPE_GUEST im Context repräsentiert.
9Sind Schema-Directives für Auth besser?
Deklarativer in Apollo/Node.js, aber framework-abhängig. In Magento gibt es keine native Unterstützung – Resolver-Guards sind der Standard.
10Wie teste ich Auth-Logik in Magento-Resolvern?
Context als Mock mit kontrollierten getUserId()/getUserType()-Werten in Unit-Tests. In Integrationstests echter Token über generateCustomerToken.