{ }
type
GraphQL · Sicherheit · Authorization · Magento · Resolver
Berechtigungen auf Feldniveau
sensible Daten in GraphQL absichern

GraphQL-Authorization endet nicht bei der Query-Prüfung. Sensible Felder wie E-Mail-Adressen, Bestellhistorien und Admin-spezifische Daten müssen auf Feldebene abgesichert werden – im Resolver-Kontext, nicht nur im Schema. Dieser Artikel zeigt, wie das in der Praxis funktioniert.

15 Min. Lesezeit Resolver-Kontext · Null-Strategie · Auth-Exceptions · Magento GraphQL · Security · Headless Commerce

1. Warum Query-Level-Auth nicht ausreicht

Die häufige Annahme lautet: wenn eine Query nur für eingeloggte Nutzer zugänglich ist, sind alle Felder darin automatisch geschützt. Das stimmt in einfachen Fällen – aber nicht, sobald ein Schema Felder enthält, die je nach Nutzerrolle unterschiedlich befüllt werden sollen. Ein Kundenkonto-Typ in Magento enthält sowohl öffentlich irrelevante Felder wie den Vornamen als auch sensible Felder wie die vollständige Adressliste, die E-Mail-Adresse oder die Bestellhistorie. Eine reine Query-Level-Auth schützt nicht davor, dass ein eingeloggter Kunde Daten eines anderen Kunden abfragt.

Hinzu kommt ein zweites Szenario: Admin-Felder. Viele GraphQL-Schemas enthalten Felder, die technisch vorhanden, aber nur für bestimmte Rollen relevant sind – interne Scores, Debug-Informationen, Customer-IDs für CRM-Systeme. Diese Felder tauchen im Schema auf und sind theoretisch abfragbar, auch wenn ein normaler Kunde keinen Zugriff haben sollte. Die Lösung liegt nicht im Ausblenden aus dem Schema, sondern in der Prüfung des Resolver-Kontexts auf Feldebene.

2. Der Resolver-Kontext als Auth-Träger

In Magento GraphQL ist der Resolver-Kontext das zentrale Auth-Objekt. Es wird bei jeder Anfrage aufgebaut und enthält die User-ID des aktuellen Kunden, den Benutzertyp (Gast, Kunde, Admin) und weitere Kontextdaten. Der Kontext ist im Resolver über den zweiten Parameter zugänglich und sollte in jedem Resolver, der auf personenbezogene Daten zugreift, überprüft werden.

Das Pattern: Der Resolver ruft zuerst $context->getUserId() auf. Ist das Ergebnis null oder zero, ist der Aufrufer nicht authentifiziert. Ist die User-ID vorhanden, prüft der Resolver, ob die angeforderten Daten dem angemeldeten Nutzer gehören. Nur wenn beide Prüfungen positiv sind, werden die sensiblen Felder befüllt. Diese Logik muss explizit im Resolver stehen – das Schema selbst kann sie nicht erzwingen, weil GraphQL-Schemas keine Laufzeit-Checks durchführen.


# Customer type with mixed sensitivity fields
type Customer {
  id: ID!
  firstname: String!
  lastname: String!
  email: String!                 # sensitive: own customer only
  addresses: [CustomerAddress]   # sensitive: own customer only
  orders: [Order]                # sensitive: own customer only
  internalCrmId: String          # admin-only: null for regular customers
  fraudScore: Float              # admin-only: null for regular customers
}

# Query entry point — authentication is checked at top-level resolver
type Query {
  customer: Customer             # requires Bearer token
}

3. Drei Muster für Feldniveau-Berechtigungen

In der Praxis haben sich drei Feldniveau-Auth-Muster etabliert, die je nach Kontext unterschiedlich geeignet sind. Das erste Muster ist die direkte Kontext-Prüfung im Resolver: der Resolver vergleicht die angeforderte Ressource mit der User-ID des Aufrufers und wirft eine GraphQlAuthorizationException, wenn die Ids nicht übereinstimmen. Das ist das sicherste Muster, weil es keine impliziten Annahmen macht.

Das zweite Muster ist die Null-Rückgabe für nicht autorisierte Felder: statt einer Exception gibt der Resolver null zurück, wenn der Aufrufer nicht berechtigt ist. Das eignet sich für optionale Admin-Felder, bei denen ein regulärer Kunde einfach keine Daten sieht, ohne einen Fehler zu erhalten. Das dritte Muster verwendet Auth-Direktiven (@auth(requires: ADMIN)) auf Schema-Ebene, die über eine Middleware interpretiert werden. Das ist deklarativ und wartbar, erfordert aber eine Middleware-Implementierung und funktioniert in Magento nicht out-of-the-box.

4. Null-Strategie: wann null, wann Exception?

Die Entscheidung zwischen null und Exception auf Feldebene hat direkte Auswirkungen auf das Frontend-Verhalten. Eine Exception bricht im Standard-GraphQL die gesamte Elternobjekt-Auflösung – wenn ein Non-Null-Feld eine Exception wirft, propagiert der Fehler aufwärts im Response-Baum und kann das gesamte Objekt auf null setzen. Das ist oft nicht gewünscht. Deshalb gilt das Prinzip: sensible Felder, die Auth-Prüfungen unterliegen, sollten nullable (String, nicht String!) deklariert werden.

Eine Auth-Exception sollte nur dann geworfen werden, wenn der gesamte Request illegitim ist – etwa wenn ein Kunde versucht, die Daten eines anderen Kunden über ein Top-Level-Query-Argument abzufragen. Bei Feldern, die für die aktuelle Nutzerrolle schlicht nicht verfügbar sind, ist null die korrektere Antwort. Der Client weiß dann: das Feld ist vorhanden, aber für mich nicht zugänglich – und kann entsprechend reagieren, ohne in einen Fehler-Zustand zu geraten.

5. Magento: Kunden-, Bestell- und Admin-Felder absichern

In Magento GraphQL gibt es mehrere kritische Stellen, an denen Feldniveau-Auth essenziell ist. Das prominenteste Beispiel ist der customer-Query: er gibt die Daten des eingeloggten Kunden zurück, basierend auf dem Bearer-Token im Authorization-Header. Der Top-Level-Resolver prüft den Kontext und lädt nur den Kunden, dessen ID mit dem Token übereinstimmt. Dennoch sind Felder wie email und addresses erst nach einer zweiten Prüfung im Feld-Resolver vollständig sicher.

Noch kritischer ist die Absicherung von Bestelldaten. Die customerOrders-Query muss nicht nur prüfen, ob der Aufrufer eingeloggt ist, sondern ob jede einzelne zurückgegebene Bestellung dem Kunden gehört. Ein Bug in diesem Bereich kann dazu führen, dass Bestelldetails eines anderen Kunden zurückgegeben werden – ein ernstes Datenschutzproblem. Das Pattern: immer eine explizite Filter-Condition auf customer_id = context.getUserId() in der Repository-Abfrage, nie nur auf dem Schema-Level oder dem Query-Entry-Point.


# Secure resolver pattern for Magento field-level authorization
# File: Model/Resolver/CustomerEmail.php

# The resolver checks both authentication and ownership:
# 1. Is the user authenticated? context.getUserId() > 0
# 2. Does the requested customer belong to this user?
# 3. Only then return the sensitive field

# Example authorization flow in pseudo-GraphQL terms:
query SecureCustomerData {
  customer {
    id          # safe: only own customer loaded at top-level resolver
    firstname   # safe: no extra auth needed
    email       # sensitive: resolver re-checks context.getUserId()
    orders {    # sensitive: repository filters by customer_id
      number
      grand_total
      status
    }
    internalCrmId   # admin-only: resolver returns null unless isAdmin()
  }
}

# Response when accessing internalCrmId as regular customer:
# { "data": { "customer": { "internalCrmId": null } } }
# No error — just null, frontend handles gracefully

6. Schema-Gestaltung für sicherheitssensible Felder

Das Schema kommuniziert implizit Erwartungen an den Client. Ein Feld, das als String! deklariert ist, verspricht: dieser Wert ist immer vorhanden. Für sicherheitssensible Felder gilt die Regel: wenn der Wert je nach Auth-Kontext fehlen kann, muss das Feld nullable sein. Das ist kein Schwächen des Schemas, sondern korrekte Modellierung der Realität. Ein Feld wie internalScore: Float signalisiert dem Client: dieses Feld kann vorhanden sein, muss aber nicht.

Ergänzend empfiehlt sich das Pattern der Typ-Trennung: Admin-spezifische Felder in einem separaten Typ AdminCustomerData auslagern, der nur über eine Admin-authentifizierte Query erreichbar ist. Das Schema selbst kann so gestaltet sein, dass die Admin-Felder für reguläre Clients gar nicht erst im öffentlichen Schema auftauchen – durch separaten Endpunkt oder durch eine Schema-Stitching-Strategie. Das ist wartbarer als Dutzende einzelner Null-Checks auf Feldebene.

7. Auth-Middleware und Direktiven: Vor- und Nachteile

Auth-Direktiven wie @auth(requires: ADMIN) sind ein eleganter Weg, Berechtigungen deklarativ im Schema auszudrücken. Statt in jeden Resolver explizite Checks einzubauen, verarbeitet eine Middleware alle Felder mit @auth-Direktive vor der eigentlichen Resolver-Ausführung. Das zentralisiert Auth-Logik und macht Sicherheitsanforderungen im Schema lesbar.

Der Nachteil: in Magento gibt es keine eingebaute Directive-Middleware für Auth. Eine eigene Implementierung ist möglich, aber aufwändig. Hinzu kommt, dass Direktiven-basierte Auth den Kontext-Zustand zur Compile-Zeit nicht kennt – dynamische Prüfungen wie "gehört diese Bestellung dem aktuellen Kunden" lassen sich nicht in einer statischen Direktive ausdrücken. Das Fazit: Direktiven eignen sich für rollenbasierte Checks (Admin vs. Kunde), nicht für ressourcenbasierte Ownership-Prüfungen. Für Letzteres bleibt die explizite Kontext-Prüfung im Resolver die sicherere und flexiblere Wahl.

8. Auth-Szenarien systematisch testen

Auth-Bugs in GraphQL sind oft schwer zu finden, weil die API in happy-path-Tests korrekt funktioniert, aber in Cross-Customer-Szenarien Daten leakt. Systematisches Testen von Feldniveau-Berechtigungen erfordert explizite Testszenarien für jeden kritischen Auth-Pfad. Das bedeutet: Test als eingeloggter Kunde A, der Daten von Kunde B versucht abzurufen. Test als Gast, der ein geschütztes Feld anfragt. Test als Kunde, der Admin-Felder anfragt. Jedes dieser Szenarien sollte einen definierten Expected Output haben – entweder null oder eine Auth-Exception.

In Magento eignen sich Integration Tests mit dem GraphQl-Test-Client aus dem Test-Framework. Pro Szenario wird ein dedizierter Kunde angelegt, ein Token generiert und eine Query abgefeuert. Das Response wird gegen die erwartete Struktur validiert. Besonders wichtig: auch nach Schema-Änderungen und Resolver-Refactorings müssen die Auth-Tests laufen. GraphQL Inspector kann zusätzlich prüfen, ob neu hinzugefügte Felder im Schema als nullable modelliert sind, wenn sie Auth-Prüfungen unterliegen.


# Test scenarios for field-level authorization

# Scenario 1: Authenticated customer requests own data — PASS
# Authorization: Bearer <customer-a-token>
query OwnData {
  customer {
    email       # returns: "customer-a@example.com"
    orders { number }   # returns: customer A's orders only
  }
}

# Scenario 2: Guest requests protected field — FAIL (exception)
# No Authorization header
query GuestAccess {
  customer {
    email       # throws GraphQlAuthorizationException
  }
}

# Scenario 3: Customer requests admin-only field — null (no exception)
# Authorization: Bearer <customer-a-token>
query AdminFieldAsCustomer {
  customer {
    fraudScore  # returns: null (customer cannot see this)
  }
}

9. Vergleich der Auth-Ansätze auf einen Blick

In realen Projekten werden oft mehrere Auth-Muster gleichzeitig eingesetzt. Die Wahl hängt von der Art der Prüfung ab: rollenbasiert oder ressourcenbasiert, deklarativ oder imperativ. Die folgende Tabelle fasst die wichtigsten Ansätze zusammen.

Auth-Ansatz Prüfungsebene Geeignet für Einschränkung
Top-Level-Resolver-Check Query-Einstiegspunkt Basis-Auth: eingeloggt vs. Gast Schützt nicht einzelne Felder
Feld-Resolver-Kontext-Check Einzelnes Feld Ownership-Prüfung, sensible Felder Wiederholung in vielen Resolvern
Null-Rückgabe Feld-Wert Optional-Felder für Rollen Erfordert nullable-Deklaration
Auth-Direktiven Schema-Deklaration Rollenbasierte Felder Kein Magento-Builtin, keine Ownership-Logik
Separate Admin-Schema-Typen Schema-Struktur Klare Trennung Admin vs. Kunde Höherer Schema-Design-Aufwand

In Magento-Projekten ist die Kombination aus Top-Level-Resolver-Check für Basis-Auth und expliziten Feld-Resolver-Checks für Ownership und Admin-Felder die bewährteste Strategie. Auth-Direktiven können ergänzend für einfache Rollenprüfungen eingesetzt werden, wenn die Infrastruktur dafür vorhanden ist. Separate Admin-Typen sind besonders dann sinnvoll, wenn das Schema öffentlich dokumentiert wird und Admin-Felder nicht im öffentlichen Schema sichtbar sein sollen.

Mironsoft

GraphQL-Sicherheit, Auth-Architektur und Magento-Resolver

Sensible Daten in GraphQL zuverlässig absichern?

Wir analysieren bestehende GraphQL-Resolver auf Auth-Lücken, implementieren Feldniveau-Berechtigungen und testen systematisch alle kritischen Auth-Szenarien – für Magento und Headless-Frontends.

Auth-Audit

Resolver auf fehlende Kontext-Prüfungen und Ownership-Lücken analysieren

Feldniveau-Auth

Kontext-Checks, Null-Strategie und Admin-Typen für sensible Felder implementieren

Auth-Testing

Cross-Customer-Szenarien, Gast-Zugriffe und Admin-Felder systematisch testen

10. Zusammenfassung

Feldniveau-Berechtigungen in GraphQL sind ein wesentlicher Bestandteil einer sicheren API-Architektur, der in vielen Projekten vernachlässigt wird. Die Basis-Erkenntnis: Authentifizierung auf Query-Ebene schützt nicht davor, dass ein eingeloggter Nutzer Daten sieht, die ihm nicht gehören. Ownership-Prüfungen, Admin-Feld-Checks und die richtige Kombination aus Exception und Null-Rückgabe müssen explizit auf Feldebene implementiert werden.

Das wichtigste Prinzip: Sicherheit ist kein Schema-Feature, sondern Resolver-Logik. Das Schema beschreibt, was theoretisch vorhanden ist. Der Resolver entscheidet bei jedem Request, was für diesen konkreten Aufrufer tatsächlich zurückgegeben wird. Wer diese Trennung versteht und konsequent umsetzt, baut GraphQL-APIs, die auch unter DSGVO-Anforderungen und Penetration-Test-Szenarien bestehen.

Feldniveau-Berechtigungen in GraphQL — Das Wichtigste auf einen Blick

Kontext-Check

context.getUserId() in jedem Feld-Resolver für personenbezogene Daten prüfen. Niemals auf Top-Level-Check allein verlassen.

Null vs. Exception

Exception für illegitime Requests. Null für nicht zugängliche Felder der aktuellen Rolle. Felder müssen nullable deklariert sein.

Ownership-Prüfung

Repository-Filter immer auf customer_id = context.getUserId() setzen. Nie nur den Query-Entry-Point prüfen.

Testing

Cross-Customer-Tests, Gast-Zugriffs-Tests und Admin-Feld-Tests als eigene Testklassen anlegen und nach jedem Refactoring ausführen.

11. FAQ: Berechtigungen auf Feldniveau in GraphQL

1Reicht Query-Authentifizierung allein?
Nein. Ownership-Prüfungen (gehört diese Ressource dem aktuellen Nutzer?) müssen auf Feldebene im Resolver implementiert werden.
2Null zurückgeben oder Exception werfen?
Null für rollenbasiert nicht zugängliche Felder. Exception für illegitime Requests (Zugriff auf fremde Ressource).
3Cross-Customer-Datenleck verhindern?
Repository-Filter immer auf customer_id = context.getUserId() setzen. Nie alle Daten laden und dann im Code filtern.
4Auth-Direktiven in Magento nutzbar?
Nicht out-of-the-box. Magento hat keine eingebaute Directive-Middleware. Eigene Implementierung möglich, aber aufwändig.
5Admin-Felder im öffentlichen Schema?
Nicht nötig. Separate Admin-Typen oder ein separater Endpunkt können Admin-Felder aus dem öffentlichen Schema auslagern.
6Auth-Szenarien systematisch testen?
Explizite Tests: Kunde A fragt Daten von Kunde B, Gast fragt geschützte Felder, Kunde fragt Admin-Felder. Jedes Szenario braucht einen definierten Expected Output.
7Sicherheitsfeld: nullable oder Non-Null?
Nullable. Non-Null-Felder mit Auth-Check können bei Fehler die gesamte Elternobjekt-Auflösung brechen. Nullable erlaubt null als korrekte Antwort.
8Welche Magento-Exception für Auth-Fehler?
GraphQlAuthorizationException – erzeugt im errors-Array category: graphql-authorization. Frontend und Monitoring können sie korrekt klassifizieren.
9Schützt Introspection-Deaktivierung?
Nein. Sie verhindert nur systematisches Schema-Erkunden. Auth-Lücken in Resolvern werden dadurch nicht behoben – nur schwerer zu finden.
10Wichtigste Sicherheitspraxisregel?
Das Schema beschreibt Möglichkeiten. Der Resolver entscheidet bei jedem Request, was für diesen Aufrufer zurückgegeben wird. Sicherheit ist Resolver-Logik, nicht Schema-Feature.