UNPKG

@pothos/plugin-scope-auth

Version:

A Pothos plugin for adding scope based authorization checks to your GraphQL Schema

119 lines (101 loc) 3.5 kB
import { isThenable, type MaybePromise, type PothosOutputFieldConfig, type SchemaTypes, } from '@pothos/core'; import type { GraphQLResolveInfo } from 'graphql'; import type { PothosScopeAuthPlugin, UnauthorizedErrorFn } from '.'; import { ForbiddenError } from './errors'; import RequestCache from './request-cache'; import { AuthScopeFailureType, type ResolveStep, type UnauthorizedResolver } from './types'; const defaultUnauthorizedResolver: UnauthorizedResolver<never, never, never, never, never> = ( _root, _args, _context, _info, error, ) => { throw error; }; export function resolveHelper<Types extends SchemaTypes>( steps: ResolveStep<Types>[], plugin: PothosScopeAuthPlugin<Types>, fieldConfig: PothosOutputFieldConfig<Types>, ) { const unauthorizedResolver = fieldConfig.pothosOptions.unauthorizedResolver ?? defaultUnauthorizedResolver; const globalUnauthorizedError = plugin.builder.options.scopeAuth?.unauthorizedError; const defaultUnauthorizedError: UnauthorizedErrorFn<Types, object, {}> = ( parent, _args, context, info, result, ) => { if (globalUnauthorizedError) { return globalUnauthorizedError(parent, context, info, result); } if ( (result.failure.kind === AuthScopeFailureType.AuthScope || result.failure.kind === AuthScopeFailureType.AuthScopeFunction) && result.failure.error ) { return result.failure.error; } return result.message; }; const createError: UnauthorizedErrorFn<Types, object, {}> = fieldConfig.pothosOptions.unauthorizedError ?? defaultUnauthorizedError; return (parent: unknown, args: {}, context: Types['Context'], info: GraphQLResolveInfo) => { let resolvedValue: unknown; const cache = RequestCache.fromContext(context, plugin.builder); function runSteps(index: number): MaybePromise<unknown> { for (let i = index; i < steps.length; i += 1) { const { run, errorMessage } = steps[i]; const stepResult = run(cache, parent, args, context, info, (val) => { resolvedValue = val; }); if (isThenable(stepResult)) { return stepResult.then((result) => { if (result) { const error = createError(parent as object, args, context, info, { message: typeof errorMessage === 'function' ? errorMessage(parent, args, context, info) : errorMessage, failure: result, }); return unauthorizedResolver( parent as never, args, context as never, info, typeof error === 'string' ? new ForbiddenError(error, result) : error, ); } return runSteps(i + 1); }); } if (stepResult) { const error = createError(parent as object, args, context, info, { message: typeof errorMessage === 'function' ? errorMessage(parent, args, context, info) : errorMessage, failure: stepResult, }); return unauthorizedResolver( parent as never, args, context as never, info, typeof error === 'string' ? new ForbiddenError(error, stepResult) : error, ); } } return resolvedValue; } return runSteps(0); }; }