dougmoscrop / serverless-http

Use your existing middleware framework (e.g. Express, Koa) in AWS Lambda 🎉
Other
1.73k stars 167 forks source link

How to access context value from Lambda Authorizer? #216

Open jatinmehrotra opened 2 years ago

jatinmehrotra commented 2 years ago

I am trying to pass some data to backend using lambda authorizer, something like this

{
    "principalId": "xxxxxxxx", // The principal user identification associated with the token send by the client.
    "policyDocument": {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": "execute-api:Invoke",
                "Effect": "Allow|Deny",
                "Resource": "arn:aws:execute-api:<regionId>:<accountId>:<appId>/<stage>/<httpVerb>/[<resource>/<httpVerb>/[...]]"
            }
        ]
    },
    "context": {
        "key": "value",
        "numKey": 1,
        "boolKey": true
    }
}

However i am not able to understand; how to fetch this using below code

const handler = serverless(app);
module.exports.handler = async (event, context) => {
  // you can do other things here
  const result = await handler(event, context);
  // and here
  return result;
};

I want a way where i can access that in my backend and pass on to my routes

vadymhimself commented 2 years ago

+1

abdennour commented 2 years ago

Oops! still open

mattispfennig commented 1 year ago

✅ A bit late, but I finally found a solution for this without any extra lib or custom code!

You can access details of the raw Lambda request under ctx.req and there is a field called apiGateway. Here you can find the original AWS Request (event and context). Because I'm using typescript, I had some typing issues and was forced to explicit type the ctx.req as any. The following code works for me

  const orgReq = ctx.req as any;
  const awsRequest = orgReq.apiGateway; // { event: {...}, context: {...}}
  const lambdaContext = awsRequest.context;
  console.debug(JSON.stringify(lambdaContext));
notsoluckycharm commented 8 months ago
  const orgReq = ctx.req as any;

This works, but if you want to skip the any... make a type.d.ts file and add

declare namespace Express {
  export interface Request {
    apiGateway: {
      event: APIGatewayProxyEvent,
      context: 
    APIGatewayEventRequestContextWithAuthorizer<APIGatewayEventLambdaAuthorizerContext<TAuthorizerContext>>;
    }
  }
}

Context is a little trickier because it does really depend on what you're using. This works for custom authorizers so you can do req.apiGateway.context.authorizer now without issue.

mmieluch commented 7 months ago

OK, so to put things together, here's my near-complete approach.

I have a main entry point in my index.ts where I set up my middleware, routes, etc. Each route handler is then in a separate module, coming from a factory function. Authorizer data in my case includes: user ID, user handle, and user name. In order to access the data, serverless-http must be aware that data of such shape will be present in the original request context, and it needs to extend the original Express request. This is how I've done it:

/**
 * /index.ts
 */
import { Request } from 'express';
import indexHandlerFactory from './handlers/index';

const app = express();

// Set up middleware.
app.use(cors());

// Set up routing.
app.get('/', indexHandlerFactory());

export const handler = serverless(app, {
  // Transform all incoming requests - assign authorizer data from Lambda request context.
  request (request: Request, event: APIGatewayProxyEvent) {
    // AuthorizerContext describes the shape of my user data and is defined in my type.d.ts.
    request.auth = event.requestContext.authorizer as AuthorizerContext;
  },
});

/**
 * type.d.ts
 */
type AuthorizerContext = {
  userId: number;
  userHandle: string;
  userName: string;
}

// Declaration merging
declare namespace Express {
  export interface Request {
    auth: AuthorizerContext;
  }
}

/**
 * ./handlers/index.ts
 */
import { Request, Response } from 'express';

export default function indexHandlerFactory() {
  return async (req: Request, res: Response): Promise<Response> => {
    const { userId, userName, userHandle } = req.auth;
  }
}

More on request & response transformations: https://github.com/dougmoscrop/serverless-http/blob/master/docs/ADVANCED.md#transformations SO answer on declaration merging: https://stackoverflow.com/a/40762463/210327 TS docs on declaration merging: https://www.typescriptlang.org/docs/handbook/declaration-merging.html