{ }
type
GraphQL · Magento · Customer API · Sicherheit · Resolver
Customer-Daten über Magento GraphQL
sicher erweitern

Eigene Felder in den Customer-Typen integrieren, Resolver mit Token-Validierung absichern, Extension Attributes korrekt exponieren und sensible Daten auf Feldebene schützen – alles in einem durchgängigen Praxisbeitrag.

15 Min. Lesezeit Schema · Resolver · Auth · Extension Attributes · Feldschutz Magento 2.4 · GraphQL · PHP 8.4

1. Warum Customer-Erweiterungen in GraphQL heikel sind

Kundendaten gehören zu den schützenswertesten Informationen in einem E-Commerce-System. Wer das Magento-Customer-Schema um eigene Felder erweitert, tut das meist mit gutem Grund: Bonuspunkte, Loyalitätsstufen, Präferenzen oder interne Segmentierungsmerkmale sollen im Headless-Frontend verfügbar sein. Der Weg dahin ist technisch überschaubar – die Sicherheitsdimension wird aber oft unterschätzt. Ein Resolver, der keine Token-Prüfung durchführt, macht aus einem harmlosen Feld sofort einen Datenleck-Vektor.

In Magento hängt die GraphQL-Sicherheit an einem zentralen Mechanismus: dem Customer-Context, der über den Bearer-Token im Authorization-Header befüllt wird. Jeder Resolver, der kundenbezogene Daten zurückgibt, muss diesen Kontext prüfen – sonst können unauthentifizierte Anfragen Daten anderer Kunden abrufen, wenn der Resolver keine explizite Zugangskontrolle implementiert. Dieser Artikel zeigt, wie man das Schema sauber erweitert, den Resolver korrekt aufbaut und dabei keine Sicherheitslücken öffnet.

2. Das Customer-Schema in Magento verstehen

Magento definiert den Customer-Typ in Magento_CustomerGraphQl/etc/schema.graphqls. Das Schema enthält bereits Standardfelder wie firstname, lastname, email und addresses. Die customer-Query ist mit einem Autorisierungs-Attribut versehen: Magento prüft intern, ob ein gültiger Token vorhanden ist, bevor der Resolver aufgerufen wird. Eigene Erweiterungen müssen auf diesen Typ aufsetzen – über extend type Customer in einer eigenen schema.graphqls-Datei im Modulverzeichnis.

Wichtig ist, zu verstehen, dass das bloße Hinzufügen eines Feldes zu Customer noch keine Sicherheit garantiert. Die eigentliche Absicherung passiert im Resolver. Magento liefert zwar den Kontext mit, aber ob der Resolver diesen Kontext auch auswertet, liegt vollständig beim Entwickler. Ein Feld, das intern auf eine EAV-Tabelle zeigt, und ein Resolver, der keine Kontext-Prüfung macht, sind eine Kombination, die in der Produktion zu Problemen führt.


# app/code/Vendor/CustomerExtension/etc/schema.graphqls
# Extend the native Customer type with a loyalty field
extend type Customer {
    loyalty_level: String @resolver(class: "Vendor\\CustomerExtension\\Model\\Resolver\\LoyaltyLevel")
    bonus_points: Int @resolver(class: "Vendor\\CustomerExtension\\Model\\Resolver\\BonusPoints")
    internal_segment: String @resolver(class: "Vendor\\CustomerExtension\\Model\\Resolver\\InternalSegment")
}

3. Extension Attributes im Schema sauber exponieren

Extension Attributes sind der Magento-eigene Mechanismus, um Datenmodelle ohne Core-Änderungen zu erweitern. Im GraphQL-Kontext bedeutet das: Das EAV-Attribut oder das Custom Attribute liegt auf dem CustomerInterface, der Resolver muss es aus der richtigen Quelle lesen. Der häufige Fehler ist, direkt auf den $value-Array im Resolver zuzugreifen und zu hoffen, dass das gewünschte Attribut dort schon vorhanden ist. Tatsächlich ist der $value-Array in Magento GraphQL der aufgelöste Parent-Wert – bei Customer-Feldern enthält er die Daten des Customer-Objekts, aber nicht automatisch alle Extension Attributes.

Die saubere Lösung ist, im Resolver auf das CustomerInterface-Objekt zuzugreifen, indem man die entity_id aus dem $value-Array entnimmt und damit das vollständige Customer-Modell über ein Repository lädt. Das garantiert, dass Extension Attributes korrekt geladen werden – inklusive etwaiger Plugin-Magie, die beim Laden greift. Wer aus Performance-Gründen mehrfaches Laden vermeiden will, kann ein Request-scoped Cache-Layer mit einer eigenen Identity-Map implementieren.

4. Den Resolver bauen: Delegation statt Logik

Ein guter GraphQL-Resolver in Magento ist dünn. Er nimmt Argumente entgegen, prüft den Kontext, delegiert die eigentliche Arbeit an einen Service oder ein Repository und gibt das Ergebnis zurück. Geschäftslogik gehört nicht in den Resolver, sondern in einen Service, der unabhängig von GraphQL testbar ist. Das gilt für Customer-Resolver genauso wie für Produkt-Resolver: Der Resolver ist der Übersetzer zwischen dem GraphQL-Layer und der Fachlogik, kein Behälter für beides.


# Query to fetch loyalty data for the authenticated customer
query GetCustomerLoyalty {
  customer {
    firstname
    email
    loyalty_level
    bonus_points
  }
}

# Mutation to redeem bonus points
mutation RedeemPoints($points: Int!) {
  redeemBonusPoints(points: $points) {
    success
    remaining_points
    message
  }
}

5. Token-Validierung und Kontext-Prüfung im Resolver

Magento stellt im Resolver-Kontext ein UserContextInterface zur Verfügung. Über $context->getUserContext()->getUserType() kann der Resolver prüfen, ob der anfragende Nutzer ein authentifizierter Kunde ist. Der User-Type UserContextInterface::USER_TYPE_CUSTOMER signalisiert, dass ein gültiger Token vorhanden ist und zu einem echten Kunden gehört. Ist der User-Type ein Gast oder ein Admin, sollte der Resolver mit einer GraphQlAuthorizationException antworten, nicht mit einem leeren Ergebnis. Leere Ergebnisse verbergen Fehler; Exceptions machen Sicherheitsprobleme sichtbar.

Zusätzlich zur User-Type-Prüfung ist es wichtig, die Customer-ID aus dem Kontext zu entnehmen und sicherzustellen, dass der Resolver nur Daten des anfragenden Kunden zurückgibt – nicht die eines anderen. Bei Customer-Feldern auf dem Customer-Typ passiert das in Magento implizit, weil die customer-Query immer den eingeloggten Kunden zurückgibt. Bei Custom-Queries, die eine Customer-ID als Argument entgegennehmen, ist die explizite Prüfung Pflicht.


# Correct: customer query always returns the authenticated customer's data
query MyAccount {
  customer {
    id
    email
    loyalty_level
    bonus_points
  }
}

# Wrong: exposing a query that accepts arbitrary customer IDs without auth check
# query GetAnyCustomer($id: Int!) {
#   customerById(id: $id) { email bonus_points }
# }
# — This would allow any caller to read any customer's data

6. Feldlevel-Schutz: was nie im Schema stehen sollte

Nicht alles, was technisch als Extension Attribute verfügbar ist, gehört in das GraphQL-Schema. Interne Scoring-Werte, Betrugserkennungsmerkmale, Roh-Passwort-Hashes oder administrative Flags sind Beispiele für Felder, die im Headless-Frontend keinen Platz haben. Die GraphQL-Introspektion ermöglicht es jedem Client, das vollständige Schema abzufragen – was im Schema ist, ist für jeden sichtbar, der Introspektion aktivieren kann. Felder, die man nicht exponieren möchte, dürfen im Schema schlicht nicht erscheinen.

Eine zweite Kategorie sind Felder, die zwar im Schema stehen dürfen, aber nur unter bestimmten Bedingungen Daten zurückgeben sollen. Beispiel: Ein loyalty_level soll nur für Kunden sichtbar sein, die am Bonusprogramm teilnehmen. Der Resolver prüft diese Bedingung und gibt null zurück, wenn sie nicht erfüllt ist. Das ist sauberer als das Feld komplett aus dem Schema zu entfernen, weil Frontend-Entwickler so wissen, dass das Feld existiert, aber situationsabhängig ist.

7. Falsch gegen Richtig: typische Sicherheitslücken

Der häufigste Fehler bei Customer-Resolver-Erweiterungen ist das Weglassen der Kontext-Prüfung. Ein Resolver, der nur den $value-Array auswertet und keinerlei Auth-Check durchführt, gibt Daten zurück, sobald der Typ aufgelöst wird – unabhängig davon, ob ein gültiger Token vorhanden ist. In Magento schützt zwar die customer-Query selbst durch ein Authorization-Attribut, aber custom Queries, die Customer-Daten über andere Einstiegspunkte exponieren, können diesen Schutz umgehen.

Szenario Falsch Richtig Risiko
Auth-Prüfung Kein Kontext-Check im Resolver getUserType() prüfen, Exception werfen Datenleck für unauthentifizierte Calls
Customer-ID ID aus Argument unkontrolliert nutzen ID aus Kontext entnehmen, Argument ignorieren IDOR – Zugriff auf fremde Kundendaten
Interne Felder Fraud-Score ins Schema aufnehmen Interne Felder nicht im Schema exponieren Introspection gibt interne Logik preis
Fehlerbehandlung null zurückgeben bei Auth-Fehler GraphQlAuthorizationException werfen Fehler bleibt unsichtbar, Client merkt nichts
Resolver-Logik Geschäftslogik direkt im Resolver Service-Delegation, Resolver delegiert nur Schwer testbar, unkontrollierbare Abhängigkeiten

8. Integrationstests für abgesicherte Customer-Queries

Customer-Resolver-Tests in Magento sind Integrationstests, die einen echten HTTP-Request gegen den GraphQL-Endpunkt absetzen. Magento stellt hierfür die GraphQlQueryTest-Basisklasse zur Verfügung. Ein guter Testaufbau für abgesicherte Customer-Felder umfasst mindestens drei Szenarien: den erfolgreichen Abruf mit gültigem Token, den abgelehnten Abruf ohne Token mit erwartetem Fehlercode, und den Versuch, mit einem fremden Token auf Daten eines anderen Kunden zuzugreifen. Letzterer Test ist der wichtigste, wird aber am häufigsten weggelassen.

Für schnelle manuelle Verifikation im Entwicklungsalltag ist Altair oder GraphiQL mit einem gespeicherten Token im Authorization-Header die effizienteste Methode. Der Token lässt sich über die generateCustomerToken-Mutation in derselben GraphiQL-Session erzeugen und direkt in den Header-Bereich eintragen. Dieses Setup erlaubt es, neue Felder sofort nach dem Schema-Deploy zu testen, ohne ein separates Testskript schreiben zu müssen.


# Step 1: Obtain a customer token
mutation Login {
  generateCustomerToken(email: "test@example.com", password: "Password1!") {
    token
  }
}

# Step 2: Use the token in Authorization header: Bearer <token>
# Then query the extended customer fields
query CustomerLoyaltyTest {
  customer {
    email
    loyalty_level
    bonus_points
  }
}

# Step 3: Test without token — expect GraphQlAuthorizationException
# Expected response: { "errors": [{ "message": "The current customer isn't authorized." }] }

9. Zusammenfassung

Customer-Daten über Magento GraphQL zu erweitern ist technisch kein aufwändiges Vorhaben – die Kombination aus extend type Customer, eigenem Resolver und DI-Konfiguration ist in wenigen Dateien erledigt. Der eigentliche Aufwand steckt in der Sicherheitsschicht: Token-Validierung, Kontext-Prüfung, die Entscheidung welche Felder überhaupt ins Schema gehören und die Sicherstellung, dass Resolver niemals Daten fremder Kunden zurückgeben. Wer diese Aspekte bewusst durchdenkt, baut eine Erweiterung, die auch in der Produktion mit realen Kundendaten zuverlässig funktioniert.

Die wichtigste Leitfrage beim Entwurf jeder Customer-GraphQL-Erweiterung lautet: Was passiert, wenn ein Angreifer diese Query ohne Token oder mit einem fremden Token aufruft? Wenn die Antwort darauf unbefriedigend ist, fehlt entweder die Auth-Prüfung im Resolver oder das Feld gehört schlicht nicht ins Schema. Sicherheit in GraphQL ist kein nachträglicher Schritt, sondern ein Designmerkmal des Resolvers.

Customer-Daten über Magento GraphQL sicher erweitern — Das Wichtigste auf einen Blick

Schema-Erweiterung

extend type Customer in eigener schema.graphqls – nur Felder aufnehmen, die tatsächlich für das Frontend benötigt werden. Interne Attribute gehören nicht ins Schema.

Resolver-Sicherheit

getUserType() immer prüfen, GraphQlAuthorizationException bei fehlendem Token. Customer-ID immer aus dem Kontext, nie aus Query-Argumenten.

Delegation

Resolver delegieren an Services und Repositories – keine Geschäftslogik im Resolver. Testbarkeit unabhängig vom GraphQL-Layer sicherstellen.

Testing

Drei Szenarien: gültiger Token, kein Token (erwartet Exception), fremder Token (erwartet keine fremden Daten). Der dritte Test wird am häufigsten vergessen.

11. FAQ: Customer-Daten über Magento GraphQL sicher erweitern

1Schützt die customer-Query automatisch alle Felder?
Die native customer-Query prüft den Token auf Query-Ebene. Custom-Queries müssen die Prüfung selbst im Resolver implementieren.
2Wie verhindere ich IDOR-Angriffe?
Customer-ID immer aus dem Kontext, nie aus Query-Argumenten. Resolver darf nur Daten des anfragenden Kunden zurückgeben.
3Wie lese ich Extension Attributes korrekt?
entity_id aus $value entnehmen, Customer-Modell über CustomerRepository laden. Nur so sind Extension Attributes vollständig verfügbar.
4Exception oder null bei Auth-Fehler?
Immer GraphQlAuthorizationException. null verbirgt den Fehler, der Client erhält kein klares Signal über die Zugriffsverweigerung.
5Welche Felder gehören nicht ins Schema?
Interne Scoring-Werte, Betrugsmerkmale, administrative Flags, Credentials. Alles, was per Introspection sichtbar wird, sollte bewusst entschieden sein.
6Extension Attributes cachen?
Ja. Ein Request-scoped Cache im Service verhindert, dass dasselbe Customer-Objekt pro Feld neu geladen wird und reduziert Datenbankabfragen.
7Wie schreibt man Integrationstests?
GraphQlQueryTest-Basisklasse nutzen. Drei Szenarien: gültiger Token, kein Token (Exception erwartet), fremder Token (keine fremden Daten).
8Feldlevel- vs. Query-Level-Schutz?
Query-Level verhindert den Aufruf ohne Token. Feldlevel entscheidet im Resolver, ob ein Feld Daten zurückgibt – beispielsweise nur für bestimmte Kundensegmente.
9Introspection in Produktion deaktivieren?
Für öffentliche APIs sinnvoll. Magento hat keine eingebaute Option; ein Nginx-Block auf __schema und __type ist ein pragmatischer Ansatz.
10Schnelltest neuer Customer-Felder im Entwicklungsalltag?
Altair oder GraphiQL mit Authorization-Header. Token per generateCustomerToken-Mutation erzeugen, in Header eintragen, sofort nach Cache-Flush testen.