serverless-http icon indicating copy to clipboard operation
serverless-http copied to clipboard

How to access context value from Lambda Authorizer?

Open jatinmehrotra opened this issue 3 years ago • 5 comments

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

jatinmehrotra avatar Oct 07 '21 10:10 jatinmehrotra

+1

vadymhimself avatar Nov 10 '21 13:11 vadymhimself

Oops! still open

abdennour avatar Apr 02 '22 22:04 abdennour

✅ 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));

mattispfennig avatar Jun 22 '23 07:06 mattispfennig

  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.

notsoluckycharm avatar Feb 07 '24 04:02 notsoluckycharm

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

mmieluch avatar Feb 13 '24 10:02 mmieluch