UNPKG

@envelop/generic-auth

Version:

This plugin allows you to implement custom authentication flow by providing a custom user resolver based on the original HTTP request. The resolved user is injected into the GraphQL execution `context`, and you can use it in your resolvers to fetch the cu

112 lines (111 loc) 5.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useGenericAuth = exports.defaultProtectSingleValidateFn = exports.defaultProtectAllValidateFn = exports.SKIP_AUTH_DIRECTIVE_SDL = exports.DIRECTIVE_SDL = exports.UnauthenticatedError = void 0; const graphql_1 = require("graphql"); const extended_validation_1 = require("@envelop/extended-validation"); class UnauthenticatedError extends graphql_1.GraphQLError { } exports.UnauthenticatedError = UnauthenticatedError; exports.DIRECTIVE_SDL = ` directive @auth on FIELD_DEFINITION `; exports.SKIP_AUTH_DIRECTIVE_SDL = ` directive @skipAuth on FIELD_DEFINITION `; function defaultProtectAllValidateFn(params) { if (params.user == null && !params.fieldAuthDirectiveNode && !params.fieldAuthExtension) { const schemaCoordinate = `${params.objectType.name}.${params.fieldNode.name.value}`; return new UnauthenticatedError(`Accessing '${schemaCoordinate}' requires authentication.`, [params.fieldNode]); } } exports.defaultProtectAllValidateFn = defaultProtectAllValidateFn; function defaultProtectSingleValidateFn(params) { if (params.user == null && (params.fieldAuthDirectiveNode || params.fieldAuthExtension)) { const schemaCoordinate = `${params.objectType.name}.${params.fieldNode.name.value}`; return new UnauthenticatedError(`Accessing '${schemaCoordinate}' requires authentication.`, [params.fieldNode]); } } exports.defaultProtectSingleValidateFn = defaultProtectSingleValidateFn; const useGenericAuth = (options) => { const contextFieldName = options.contextFieldName || 'currentUser'; if (options.mode === 'protect-all' || options.mode === 'protect-granular') { const directiveOrExtensionFieldName = options.directiveOrExtensionFieldName ?? (options.mode === 'protect-all' ? 'skipAuth' : 'auth'); const validateUser = options.validateUser ?? (options.mode === 'protect-all' ? defaultProtectAllValidateFn : defaultProtectSingleValidateFn); const extractAuthMeta = (input) => { return { fieldAuthExtension: input.extensions?.[directiveOrExtensionFieldName], fieldAuthDirectiveNode: input.astNode?.directives?.find(directive => directive.name.value === directiveOrExtensionFieldName), }; }; return { onPluginInit({ addPlugin }) { addPlugin((0, extended_validation_1.useExtendedValidation)({ rules: [ function AuthorizationExtendedValidationRule(context, args) { const user = args.contextValue[contextFieldName]; const handleField = (fieldNode, objectType) => { const field = objectType.getFields()[fieldNode.name.value]; if (field == null) { // field is null/undefined if this is an introspection field return; } const { fieldAuthExtension, fieldAuthDirectiveNode } = extractAuthMeta(field); const error = validateUser({ user, fieldNode, objectType, fieldAuthDirectiveNode, fieldAuthExtension, }); if (error) { context.reportError(error); } }; return { Field(node) { const fieldType = (0, graphql_1.getNamedType)(context.getParentType()); if ((0, graphql_1.isIntrospectionType)(fieldType)) { return false; } if ((0, graphql_1.isObjectType)(fieldType)) { handleField(node, fieldType); } else if ((0, graphql_1.isUnionType)(fieldType)) { for (const objectType of fieldType.getTypes()) { handleField(node, objectType); } } else if ((0, graphql_1.isInterfaceType)(fieldType)) { for (const objectType of args.schema.getImplementations(fieldType).objects) { handleField(node, objectType); } } return undefined; }, }; }, ], })); }, async onContextBuilding({ context, extendContext }) { const user = await options.resolveUserFn(context); extendContext({ [contextFieldName]: user, }); }, }; } if (options.mode === 'resolve-only') { return { async onContextBuilding({ context, extendContext }) { const user = await options.resolveUserFn(context); extendContext({ [contextFieldName]: user, }); }, }; } return {}; }; exports.useGenericAuth = useGenericAuth;