UNPKG

@jcm/nexus-plugin-field-authentication

Version:

Adds a new field config "authentication" that should be used to determine if the field should be resolved or not

105 lines 6.12 kB
import { plugin, core } from 'nexus'; // Most of this code is inspired on the authorize plugin from nexus const fieldAuthenticationPluginResolverImport = core.printedGenTypingImport({ module: '@jcm/nexus-plugin-field-authentication', bindings: ['FieldAuthenticationResolver', 'FieldAuthenticationResolverReturnValue'], }); const fieldDefTypes = core.printedGenTyping({ optional: true, name: 'authentication', description: ` Authentication for an individual field. Returning "true" or "Promise<true>" means the field can be accessed only if the request is authenticated. Returning "false" or "Promise<false>" means the field can be acessed only if the request is *NOT* authenticated. It's also possible to return a tuple "[boolean, Error | ResolveValue]" where the first element specifies if the request should be authenticated or not and the second specifies the behavior: If the second element is an error, this error will be returned for the field, even if throwErrorOnFailedAuthentication is false. If the second element is anything else, this value will be resolved if the request fails the authentication check. `, type: 'FieldAuthenticationResolver<TypeName, FieldName> | FieldAuthenticationResolverReturnValue', imports: [fieldAuthenticationPluginResolverImport], }); export const defaultFormatError = ({ error }) => error; // This is much more complex than it could be // but it was done that way so that it handle all scenarios possible that we might ever encounter. export function fieldAuthenticationPlugin(pluginConfig = {}) { const { formatError = defaultFormatError, throwErrorOnFailedAuthenticationByDefault = false, defaultErrorMessage = 'Not Authenticated', defaultResolveValue = null, isLogged = (_root, _args, ctx, _info) => { var _a; return !!((_a = ctx === null || ctx === void 0 ? void 0 : ctx.state) === null || _a === void 0 ? void 0 : _a.user); }, } = pluginConfig; const ensureError = (root, args, ctx, info) => (error) => { const finalErr = formatError({ error, root, args, ctx, info }); if (finalErr instanceof Error) { throw finalErr; } ; (ctx.logger || console).error(`Non-Error value ${JSON.stringify(finalErr)} returned from custom formatError in field authentication plugin`); throw new Error(defaultErrorMessage); }; return plugin({ name: 'FieldAuthentication', // we want to add a extension description: 'Makes sure request is authenticated before calling next resolvers in the chain', fieldDefTypes: fieldDefTypes, onCreateFieldResolver(config) { var _a, _b, _c; const authentication = (_c = (_b = (_a = config.fieldConfig.extensions) === null || _a === void 0 ? void 0 : _a.nexus) === null || _b === void 0 ? void 0 : _b.config) === null || _c === void 0 ? void 0 : _c.authentication; if (typeof authentication !== 'undefined') { // The authentication wrapping resolver. return function (root, args, ctx, info, next) { const { fieldName, parentType: { name: parentTypeName }, } = info; const processAuthenticationResult = (isUserLogged, result) => { const finalFormatError = ensureError(root, args, ctx, info); if (typeof result === 'boolean' || (Array.isArray(result) && result.length === 2 && typeof result[0] === 'boolean')) { const canProceed = isUserLogged === result || (Array.isArray(result) && isUserLogged === result[0]); // all branches here must return finalFormatError if (!canProceed) { if (Array.isArray(result)) { if (result[1] instanceof Error) { return finalFormatError(result[1]); } return result[1]; } else if (throwErrorOnFailedAuthenticationByDefault) { return finalFormatError(new Error(defaultErrorMessage)); } return defaultResolveValue; } return next(root, args, ctx, info); } else { return finalFormatError(new Error(`Field authentication for ${parentTypeName}.${fieldName} expected a boolean or [boolean, resolvedValue] tuple, saw ${JSON.stringify(result)} instead`)); } }; let toCompleteIsLogged; try { toCompleteIsLogged = isLogged(root, args, ctx, info); } catch (e) { toCompleteIsLogged = Promise.reject(e); } return plugin.completeValue(toCompleteIsLogged, (isUserLogged) => { if (typeof authentication !== 'function') { return processAuthenticationResult(isUserLogged, authentication); } let toComplete; try { toComplete = authentication(root, args, ctx, info); } catch (e) { toComplete = Promise.reject(e); } return plugin.completeValue(toComplete, processAuthenticationResult.bind(undefined, isUserLogged), (err) => { ensureError(root, args, ctx, info)(err); }); }, (err) => { ensureError(root, args, ctx, info)(err); }); }; } }, }); } //# sourceMappingURL=index.js.map