From f5c139d0c49c8165ddfe743f7a3449858d457d43 Mon Sep 17 00:00:00 2001 From: David Thyresson Date: Thu, 8 Jun 2023 17:29:12 -0400 Subject: [PATCH] feature: Support Auth in Fastify GraphQL Server by mimicking Lambda Events and Context (#8533) * WIP to see if graphql auth can work * Remove debugs statements * refactoring and documenting * Small refactor * plugins are needed to transform a Fastify Request to a Lambda event * Remove unhelpful casts --- packages/fastify/src/graphql.ts | 35 ++++++++++++++++++-- packages/fastify/src/lambda/index.ts | 2 +- packages/graphql-server/src/globalContext.ts | 17 ++++++++++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/packages/fastify/src/graphql.ts b/packages/fastify/src/graphql.ts index 86e9ce8a6488..c47eb478b1d2 100644 --- a/packages/fastify/src/graphql.ts +++ b/packages/fastify/src/graphql.ts @@ -1,17 +1,44 @@ +import fastifyUrlData from '@fastify/url-data' import type { FastifyInstance, HookHandlerDoneFunction } from 'fastify' +import fastifyRawBody from 'fastify-raw-body' -import type { GraphQLYogaOptions } from '@redwoodjs/graphql-server' import { createGraphQLYoga } from '@redwoodjs/graphql-server' +import type { GraphQLYogaOptions } from '@redwoodjs/graphql-server' + /** - * Encapsulates the routes + * Transform a Fastify Request to an event compatible with the RedwoodGraphQLContext's event + * which is based on the AWS Lambda event + */ +import { lambdaEventForFastifyRequest as transformToRedwoodGraphQLContextEvent } from './lambda/index' + +/** + * Redwood GraphQL Server Fastify plugin based on GraphQL Yoga + * + * Important: Need to set DISABLE_CONTEXT_ISOLATION = 1 in environment variables + * so that global context is populated correctly and features such as authentication + * works properly. + * + * It is critical to set shouldUseLocalStorageContext correctly so that the `setContext` function + * in the `useRedwoodPopulateContext` plugin sets the global context correctly with any + * extended GraphQL context as is done with `useRedwoodAuthContext` that sets + * the `currentUser` in the context when used to authenticate a user. + * + * See: packages/graphql-server/src/globalContext.ts + * * @param {FastifyInstance} fastify Encapsulated Fastify Instance - * @param {Object} options plugin options, refer to https://www.fastify.io/docs/latest/Reference/Plugins/#plugin-options + * @param {GraphQLYogaOptions} options GraphQLYogaOptions options used to configure the GraphQL Yoga Server */ export async function redwoodFastifyGraphQLServer( fastify: FastifyInstance, options: GraphQLYogaOptions, done: HookHandlerDoneFunction ) { + // These two plugins are needed to transform a Fastify Request to a Lambda event + // which is used by the RedwoodGraphQLContext and mimics the behavior of the + // api-server withFunction plugin + fastify.register(fastifyUrlData) + await fastify.register(fastifyRawBody) + try { const { yoga } = createGraphQLYoga(options) @@ -22,6 +49,8 @@ export async function redwoodFastifyGraphQLServer( const response = await yoga.handleNodeRequest(req, { req, reply, + event: transformToRedwoodGraphQLContextEvent(req), + requestContext: {}, }) for (const [name, value] of response.headers) { diff --git a/packages/fastify/src/lambda/index.ts b/packages/fastify/src/lambda/index.ts index 248d08025d3a..c8029e64a646 100644 --- a/packages/fastify/src/lambda/index.ts +++ b/packages/fastify/src/lambda/index.ts @@ -110,7 +110,7 @@ export async function lambdaRequestHandler( return requestHandler(req, reply, LAMBDA_FUNCTIONS[routeName]) } -function lambdaEventForFastifyRequest( +export function lambdaEventForFastifyRequest( request: FastifyRequest ): APIGatewayProxyEvent { return { diff --git a/packages/graphql-server/src/globalContext.ts b/packages/graphql-server/src/globalContext.ts index 38e9101c440a..eb14e1c7bb7c 100644 --- a/packages/graphql-server/src/globalContext.ts +++ b/packages/graphql-server/src/globalContext.ts @@ -19,6 +19,23 @@ export interface GlobalContext extends Record {} let GLOBAL_CONTEXT: GlobalContext = {} let PER_REQUEST_CONTEXT: AsyncLocalStorage> +/** + * + * You must have shouldUseLocalStorageContext return true + * when you're self-hosting RedwoodJS. + * + * It is critical to set this correctly so that the `setContext` function + * in the `useRedwoodPopulateContext` plugin sets the global context + * correctly with any extended GraphQL context as is done with + * `useRedwoodAuthContext` that sets the `currentUser` in the context when + * used to authenticate a user. + * + * This will ensure that the GraphQLHandler will use the per-request context. + * + * You do not need to use LocalStorageContext for AWS (Netlify/Vercel) + * because each Lambda request is handled individually. + * + */ export const shouldUseLocalStorageContext = () => process.env.DISABLE_CONTEXT_ISOLATION !== '1'