apollo-server-integrations / apollo-server-integration-aws-lambda

An integration to use AWS Lambda as a hosting service with Apollo Server
MIT License
46 stars 9 forks source link

Require Custom Express Middleware for Cookie Authentication #83

Closed Nosherwan closed 1 year ago

Nosherwan commented 1 year ago

Essentially I am running the below code for cookie authorisation in version 3. I am using custom express middleware for this that uses cookie parser to parse incoming cookies. Then I use the context function to parse the cookie extract info. I believe as custom express is not available in the latest version of the Lambda integration package hence this is not possible in version 4.

Am I right?

import { ApolloServer } from 'apollo-server-lambda';
import { typeDefs } from './type-defs';
import { resolvers } from './resolvers';
import { appKeyOne } from '../lib/config';
import express from 'express';
import cookieParser from 'cookie-parser';

const apolloServer = new ApolloServer({
    typeDefs,
    resolvers,
    context: ({ event, context, express: { req, res } }) => {
        const { headers, signedCookies } = req;
        const cookie: any = JSON.parse(signedCookies.auth);
        if (cookie?.expires_on) {
            console.log('enhanceApolloContext', cookie);
            if (cookie.token) {
            const   decodedToken = Jwt.verify(cookie.token, jwtSecret);
            }        
        }    
    }
,});

export default apolloServer.createHandler({
    expressAppFromMiddleware(middleware) {
        const app = express();
        app.use(cookieParser(appKeyOne));
        app.use(middleware);
        return app;
    },});
BlenderDude commented 1 year ago

Welcome from Reddit! I want to lay down a few principles to make sure we are on the same page.

One of the big pillars of the AS4 rewrite, is that it splits apart the GraphQL server and the request handlers. This is what allows AS4 to have such a vast library of 3rd party integrations. Essentially, nothing on ApolloServer will be able to "listen" to http requests. Even the express and standalone handlers that are part of the package, don't dive into the AS4 internals.

With that in mind, the goal of this library is to be as lightweight as possible. There are other libraries out there that can handle express -> serverless mutation. See Serverless Express for that. If you want to rewrite as little code as possible, that is your best solution. With that being said, if the sole purpose of express is middleware and is only hosting a one-endpoint GraphQL server, I recommend rewriting to utilize this library.

In the context of this library, you will utilize middleware to do the cookie manipulation and can access it in the context creation function.

Here is an example where middleware will regenerate a fresh cookie with each request, and utilize that cookie in context generation. You will have to do a bit more "low level" cookie reading directly on the header (I recommend the cookie package), and return the new cookie using the result.cookies array provided by AWS lambda.

import {
  startServerAndCreateLambdaHandler,
  handlers,
} from '@as-integrations/aws-lambda';
import type { APIGatewayProxyEventV2 } from 'aws-lambda';
import { server } from './server';

async function regenerateCookie(event: APIGatewayProxyEventV2) {
  // ...
  return 'NEW_COOKIE';
}

export default startServerAndCreateLambdaHandler(
  server,
  handlers.createAPIGatewayProxyEventV2RequestHandler(),
  {
    context: (event) => {
      const user = userFromCookies(event.headers.cookie)
      return {
         user,
      }
    },
    middleware: [
      // Both event and result are intended to be mutable
      async (event) => {
        const cookie = await regenerateCookie(event);
        return (result) => {
          result.cookies.push(cookie);
        };
      },
    ],
  },
);

Let me know if this helps!

Nosherwan commented 1 year ago

Thankyou @BlenderDude you have answered my question thoroughly. Yes my sole purpose here is just to read incoming cookies & then sending back cookies as well. I was not aware that the event will contain the headers in the request with the cookies. I thought event was just the Lambda event.

The serverless-express package looks interesting. I can understand AS4 goals, however my goal is to have code that I can potentially pick up and put on azure or google app engine & have the least amount of dependency on any platform; so essentially loose coupling.

For now I think you have resolved my issue, I will try to switch over to this integration library, if I have issues I will reach out further.

Nosherwan commented 1 year ago

@BlenderDude is there a way to create & push cookies from a resolver instead of the middleware?

BlenderDude commented 1 year ago

@Nosherwan Not currently without hacking into the request object. Can you make that request in a separate issue and I can implement it? Took a quick look and it doesn't seem difficult to implement

ezeikel commented 11 months ago

@BlenderDude Looking for a simple way to do this from resolvers too. Seem to be in a similar situation as @Nosherwan as Im moving away from apollo-server-lambda and having access to res.

Was there ever a separate issue created as suggested above?