UNPKG

@theguild/federation-composition

Version:
60 lines (59 loc) 2.84 kB
import { GraphQLError } from "graphql"; import { parseFields } from "../../../subgraph/helpers.js"; import { TypeKind } from "../../../subgraph/state.js"; import { ensureAccessToSelectionSet, ProvisionedAccess, } from "./auth-on-requires-rule.js"; export function AuthOnContextRule(context, supergraph) { return { ObjectTypeField(objectTypeState, fieldState) { for (const { graphId, fromContext } of contextualArgs(fieldState)) { const subgraphState = context.subgraphStates.get(graphId); const selectionSet = parseFields(fromContext.selection); if (!subgraphState || !selectionSet) { continue; } const provisionedAccess = new ProvisionedAccess(objectTypeState, fieldState); for (const providerName of findContextProviders(subgraphState, fromContext.context)) { const providerType = supergraph.objectTypes.get(providerName) ?? supergraph.interfaceTypes.get(providerName) ?? supergraph.unionTypes.get(providerName); if (!providerType || !ensureAccessToSelectionSet(supergraph, providerType, selectionSet, provisionedAccess)) { continue; } context.reportError(createContextAccessRequirementError(context.graphIdToName(graphId), `${objectTypeState.name}.${fieldState.name}`, `${context.graphIdToName(graphId)}__${fromContext.context}`)); return; } } }, }; } function contextualArgs(fieldState) { const args = []; for (const arg of fieldState.args.values()) { for (const [graphId, argInGraph] of arg.byGraph) { if (argInGraph.fromContext) { args.push({ graphId, fromContext: argInGraph.fromContext }); } } } return args; } function findContextProviders(subgraphState, contextName) { const providers = []; for (const [typeName, typeState] of subgraphState.types) { if ((typeState.kind === TypeKind.OBJECT || typeState.kind === TypeKind.INTERFACE || typeState.kind === TypeKind.UNION) && typeState.contexts.has(contextName)) { providers.push(typeName); } } return providers; } function createContextAccessRequirementError(graphName, fieldCoordinate, contextName) { return new GraphQLError(`[${graphName}] Field "${fieldCoordinate}" does not specify necessary @authenticated, @requiresScopes and/or @policy auth requirements to access the transitive data in context ${contextName} from @fromContext selection set.`, { extensions: { code: "MISSING_TRANSITIVE_AUTH_REQUIREMENTS", }, }); }