UNPKG

@accounter/server

Version:
100 lines 4.44 kB
import { defaultFieldResolver, GraphQLError } from 'graphql'; import { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils'; import { AuthContextProvider } from '../providers/auth-context.provider.js'; function extractModuleIdFromResolver(resolve) { if (typeof resolve !== 'function') { return undefined; } const resolverWithSymbols = resolve; for (const symbol of Object.getOwnPropertySymbols(resolve)) { const metadata = resolverWithSymbols[symbol]; if (metadata && typeof metadata === 'object' && 'moduleId' in metadata && typeof metadata.moduleId === 'string') { return metadata.moduleId; } } return undefined; } function resolveInjector(context, moduleId) { if (context?.injector) { return context.injector; } if (context?.ɵinjector) { return context.ɵinjector; } if (typeof context?.ɵgetModuleContext !== 'function') { return undefined; } const moduleCandidates = [moduleId, 'auth'].filter((candidate) => !!candidate); for (const candidate of moduleCandidates) { try { const moduleContext = context.ɵgetModuleContext(candidate, context); if (moduleContext?.injector) { return moduleContext.injector; } } catch { // Ignore invalid module candidate and continue with the next fallback. } } return undefined; } function throwAuthInfrastructureError(reason) { console.error(`[auth-directives] ${reason}`); throw new GraphQLError('Authentication subsystem unavailable', { extensions: { code: 'INTERNAL_SERVER_ERROR' }, }); } export function authDirectiveTransformer(schema) { return mapSchema(schema, { [MapperKind.OBJECT_FIELD]: fieldConfig => { const requiresAuthDirective = getDirective(schema, fieldConfig, 'requiresAuth')?.[0]; const requiresRoleDirective = getDirective(schema, fieldConfig, 'requiresRole')?.[0]; const requiresAnyRoleDirective = getDirective(schema, fieldConfig, 'requiresAnyRole')?.[0]; if (!requiresAuthDirective && !requiresRoleDirective && !requiresAnyRoleDirective) { return fieldConfig; } const { resolve = defaultFieldResolver } = fieldConfig; const moduleId = extractModuleIdFromResolver(resolve); return { ...fieldConfig, resolve: async (source, args, context, info) => { const injector = resolveInjector(context, moduleId); if (!injector) { throwAuthInfrastructureError(`Failed to resolve injector for auth directives (moduleId: ${moduleId ?? 'unknown'})`); } const authProvider = injector.get(AuthContextProvider); if (!authProvider) { throwAuthInfrastructureError(`AuthContextProvider is not available in injector (moduleId: ${moduleId ?? 'unknown'})`); } const authContext = await authProvider.getAuthContext(); if (!authContext?.user) { throw new GraphQLError('Authentication required', { extensions: { code: 'UNAUTHENTICATED' }, }); } if (requiresRoleDirective) { const role = requiresRoleDirective.role; if (authContext.user.roleId !== role) { throw new GraphQLError(`Requires role: ${role}`, { extensions: { code: 'FORBIDDEN' }, }); } } if (requiresAnyRoleDirective) { const roles = requiresAnyRoleDirective.roles; if (!roles.includes(authContext.user.roleId)) { throw new GraphQLError(`Requires one of roles: ${roles.join(', ')}`, { extensions: { code: 'FORBIDDEN' }, }); } } return resolve(source, args, context, info); }, }; }, }); } //# sourceMappingURL=auth-directives.js.map