amazon-web-servicesjwtserverless-frameworklambda-authorizer

Serverless AWS - is it worth using custom authorizers (as a lambda)?


Hey I am getting started with the serverless framework, apigateway, lambdas and authorizers.

Here my questions:

  1. In order to verify a proper JWT token (which seems nowadays the best solution for serverless authentication), I would need a client id. Since the authorizer lambda is not capable(?) of reaching any other parameters of the request into the lambda except for the token itself, this is a difficult task to do. How can I achieve this?

  2. Since with every authenticated call, an additional lambda is called, my costs are doubled?! May be I am misunderstanding something here, but it seems to me like implementing a method for verifying my token without using the authorizer is cheaper and I don't need to implement the authorizer lambda itself.


Solution

  • The whole point of OAuth2 is that at the point of authorization you don't care who the bearer presenting you with a token is or which client they are using (web app, Postman, etc.), only that the token is valid for whatever the token bearer is trying to do. You trust that the authentication has happened because the token issuer has done the authentication.

    When you talk about the aud(audience) (from comments) in JWTs, which may or may not be included dependent on the JWT issuer, this is intended to reflect the service or services that the JWT is valid for.

    This could for example have a value of 'myAPIName' or 'myAPIName/test' or even ['myAPIName/test1', 'myAPIName/test2'].

    If you need to validate an individual aud claim you have two choices, you can either have different Lambdas authorizing different api routes and methods with hardcoded aud variables or you can get the name of the api being called and map it back to something that would match an aud claim.

    For example the method arn for the incoming request can be found in event.methodArn. This looks like arn:aws:execute-api:{regionId}:{accountId}:{apiId}/{stage}/{httpVerb}/ potentially with [{resource}/[{child-resources}]] dependent on your implementation. With a bit of string manipulation, you could map this back to the format of an audience claim (whatever that looks like for you).

    If you would rather work an api name instead of an api name you can use the apigateway.getrestapi method for whatever sdk you are using. Documentation on this method for the JavaScript sdk can be found here.

    The JWT may have a sub(subject claim), again dependent on implementation of the JWT issuer, this could relate to any of your users (or at least the users the JWT issuer knows about). Validating this beyond checking the signature of the JWT is pointless. The best you could do is check if that user exists and only if you have access to the same user database that the JWT issuer does. Although this can be used to ensure that UserA only has access to UserA's data. Again, you are trusting that the fact a bearer has a token is proof that they have authenticated (proved who they are).

    I hope that answers part one of your question.

    In regards to part 2, the advantage of using a Lambda Authorizer over doing the authorization in the target Lambda is caching.

    So envisage I have a token valid for one hour and I call your API once every second for 30 minutes (1,800 calls). You have a Lambda authorizer with a response cache time of 10 minutes. That means you check the JWT 3 times for 1800 api calls.

    But if you do the validation of that token in your target Lambda, you are doing that processing 1,800 times.