aws / aws-sam-cli

CLI tool to build, test, debug, and deploy Serverless applications using AWS SAM
https://aws.amazon.com/serverless/sam/
Apache License 2.0
6.5k stars 1.17k forks source link

Feature Request: API Gateway Authorizer support in SAM Local #137

Closed bitforth closed 1 year ago

bitforth commented 7 years ago

I'm using the tool to spin up locally an API gateway and run my lambda functions. However, I don't know how to configure an authorizer in template.yml

In my template.yml file, I have the following configuration:

JwtAuthorizerFunction:
    Type: AWS::Serverless::Function
    Properties:
      Environment:
        Variables:
          JWT_SECRET: '<<secret>>'
      Handler: authorizers.jwt
      Runtime: nodejs6.10
      Role:
        Fn::ImportValue:
          !Join ['-', [!Ref 'ProjectId', !Ref 'AWS::Region', 'LambdaTrustRole']]

If I understand the authorizers documentation correctly, I should be able to add the following configurations in order to configure my authorizer

Authorizer: 
  Type: "AWS::ApiGateway::Authorizer"
  Properties: 
    AuthorizerCredentials: 
      Fn::GetAtt: 
        - "LambdaInvocationRole"
        - "Arn"
    AuthorizerResultTtlInSeconds: "300"
    AuthorizerUri: "arn:aws:lambda:us-east-1:<<myarn>>:function:<<lambda function arn>>"
    Type: "TOKEN"
    IdentitySource: "method.request.header.Authorization" 
    Name: "DefaultAuthorizer"
    RestApiId: 
      Ref: "RestApi"

Everything up until that point works fine. If I run sam validate the output is Valid! sam local start-api executes without a problem.

However, I don't know how to configure the custom authorizer in my lambda functions. I've read the AWS::Serverless::Function documentation but I couldn't find anything relevant to my issue. Furthermore, there are two issues about adding support for AuthorizationType to the API but it's unclear whether this support will ever be added, or if it has been added already.

Any help pointing in the right direction is greatly appreciated!

PaulMaddox commented 7 years ago

Hi @alanchavez88,

Currently configuring a custom authorizer isn't supported when running locally with SAM Local.

I'll leave this issue open as a feature request for our backlog.

Thanks

bitforth commented 7 years ago

Thanks @PaulMaddox for the response!

If I understand the model correctly, it shouldn't matter if my custom authorizer does not run locally, right? In AWS, only authorized requests can trigger the lambda function, correct?

sanathkr commented 7 years ago

Yes, an authorization request should trigger the Lambda in AWS cloud

ghost commented 7 years ago

I'm wondering how I may use a Cognito Authorizer, is there sample for that? I've gone through all the 5 samples of SAM template.ymls on the internet and am not able to see how I may do so.

Should I just rely on the swagger export? I am migrating an existing hand-build API Gateway to SAM. Thanks for help!

bradwoods commented 6 years ago

I'm also looking for examples of how to integrate a Cognito Authorizer with lambdas made via SAM templates.

mohithg commented 6 years ago

This is a good to have feature in SAM Local, so we can test the authorization functions before deploying

sujithsnair86 commented 6 years ago

Hi is there a resolution yet for including Custom Authoriser lambda over SAM template itself or is this still a feature request AWS SAM Team is working on?

ThomasSmWatson commented 6 years ago

My database url will not work as it contains '=' and other special characters, int he env variables. How would I get around this? This is a reliant to my company

gregaeq commented 6 years ago

Interested in this feature as well. Attempting to use sam-local to auth with Cognito before making subsequent requests.

kanchen commented 6 years ago

Same here. Anyone knows the timeline to support SAM local custom authorizer ?

margic commented 6 years ago

@alanchavez88 did you ever figure out how to configure an authorizer, I notice that the question was never answered. It went off on a tangent about Sam local.

bitforth commented 6 years ago

@margic no, I never figured it out. I ended up assuming that it worked and moved on :( If you find a way to do it please post it here.

kind3r commented 6 years ago

Hi, I wrote a small tutorial about how I use custom authorizers. While this does not work when running locally, it does work when being deployed. Hope it helps.

Add an API Gateway custom authorizer

Resource summary:

ApiGateway - AWS::Serverless::Api - The API definition
ApiGatewayAuthorizerRole - AWS::IAM::Role - Role for API Gateway to be able to invoke our custom authorizer function
CustomAuthorizerFunction - AWS::Serverless::Function - The Authorizer function
CustomAuthorizerFunctionRole - AWS::IAM::Role - Role of the custom authorizer function to be able to be invoked and access needed resources

ApiGateway invokes CustomAuthorizerFunction which returns a policy that will allow or deny the further invocation of your path function. For more details see Introducing custom authorizers in Amazon API Gateway

Step by step

ApiGateway:
  Type: AWS::Serverless::Api
  Properties:
    StageName: prod
    DefinitionBody:
      swagger: "2.0"
      info:
        title: 
          Ref: AWS::StackName
        description: My API that uses custom authorizer
        version: 1.0.0
      securityDefinitions:
        ...
      paths:
        ...
securityDefinitions:
  CustomAuthorizer:
    type: apiKey
    name: Authorization
    in: header
    x-amazon-apigateway-authtype: custom
    x-amazon-apigateway-authorizer:
      type: token
      authorizerUri:
        Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${CustomAuthorizerFunction.Arn}/invocations
      authorizerCredentials:
        Fn::Sub: ${ApiGatewayAuthorizerRole.Arn}
      authorizerResultTtlInSeconds: 60
paths:
  ...
  "/somepath":
    post:
      security:
        - CustomAuthorizer: []
      ...
CustomAuthorizerFunction:
  Type: AWS::Serverless::Function
  Properties:
    Runtime: nodejs6.10
    Handler: index.handler
    ...
    Role:
      Fn::Sub: ${CustomAuthorizerFunctionRole.Arn}
ApiGatewayAuthorizerRole:
  Type: AWS::IAM::Role
  Properties:
    AssumeRolePolicyDocument: 
      Version: "2012-10-17"
      Statement: 
        - 
          Effect: "Allow"
          Principal: 
            Service: 
              - "apigateway.amazonaws.com"
          Action: 
            - sts:AssumeRole
    Policies: 
      - 
        PolicyName: "InvokeAuthorizerFunction"
        PolicyDocument: 
          Version: "2012-10-17"
          Statement: 
            - 
              Effect: "Allow"
              Action:
                - lambda:InvokeAsync
                - lambda:InvokeFunction
              Resource:
                Fn::Sub: ${CustomAuthorizerFunction.Arn}
CustomAuthorizerFunctionRole:
  Type: AWS::IAM::Role
  Properties:
    AssumeRolePolicyDocument: 
      Version: "2012-10-17"
      Statement: 
        - 
          Effect: "Allow"
          Principal: 
            Service: 
              - "lambda.amazonaws.com"
          Action: 
            - sts:AssumeRole
    ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
    Policies:
      ...
Jun711 commented 6 years ago

Is there any example for cognito authorizer?

sanathkr commented 6 years ago

@kind3r This is an excellent tutorial. Something a lot of people will find it very useful. Will you be interested in submitting this as a pull request to the SAM Repo docs please? https://github.com/awslabs/serverless-application-model/tree/develop/docs

nk2580 commented 6 years ago

given that the swagger support is for version 2 and not OpenAPI 3 i would expect this feature to come out in the next release. seems like a HUGE oversight in terms of API definition to not allow authorisation in SAM templates.

have spent the last two days trying to get the version 2 swagger def to work in SAM, long story short. i may as well write a full cloudformation stack.

i will definitely be looking into other services soon if this feature isn't implemented.

setuk commented 6 years ago

Got this working - thanks!

OndeVai commented 5 years ago

I can't find a simple example of using a Cognito authorizer in an Api event event of a lambda function

abiodunjames commented 5 years ago

None of the examples given here worked for me. I finally followed this https://github.com/awslabs/serverless-application-model/blob/master/examples/2016-10-31/api_lambda_request_auth/template.yaml and i got it working.

My custom authorizer cloud formation definition:

RestApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      Auth:
        DefaultAuthorizer: ApiAuthorizer
        Authorizers:
          ApiAuthorizer:
            FunctionPayloadType: TOKEN
            FunctionArn: !GetAtt CustomAuthorizerFunction.Arn
            FunctionInvokeRole: !GetAtt CustomAuthorizerFunctionRole.Arn
            Identity:
              Headers:
              - Authorization
              ReauthorizeEvery: 300
nk2580 commented 5 years ago

For those playing at home, this relies on the idtoken which cannot be explicitly invalidated. If you never want to log a user out by invalidating the token then it’s a great fit. But if you’re needing to invalidate a token on behalf of a user then you should implement a custom authoriser that checks the access token

GeekOnGadgets commented 5 years ago

Does anyone know if we can harcode function arn in FunctionArn?

nk2580 commented 5 years ago

Not sure why you would want to do that.. are you defining an authoriser in the same Sam template as the inline swagger markup?

FWIW I found the best way to manage a custom authoriser was to make a separate cloudformation or Sam stack with that function on its own and then output the ARN. Then reference it in the Sam templates that have swagger definitions for authorisation

GeekOnGadgets commented 5 years ago

@nk2580 thanks for replying. Sorry that was pure for testing purpose. I have a separate sam stack for custom authoriser which gets deployed and then a separate stack for functions. But somehow it was not working and was unable to find any example. So decided to give a try with hardcode one.

custom autoriser sam template.yml

AWSTemplateFormatVersion: "2010-09-09"
Transform: "AWS::Serverless-2016-10-31"

Description: >-   
  A custom authoriser to verify JWT token.

Globals:
  Function:
    Runtime: nodejs8.10

Resources:
  Authoriser:
    Type: "AWS::Serverless::Function"
    Properties:
      FunctionName: test-authoriser
      CodeUri: src/
      Handler: index.handler

Outputs:
  AuthoriserArn:
    Description: The Arn of the created autoriser
    Value: !GetAtt Authoriser.Arn

and below is my another stack where I want to reference the custom authoriser

AWSTemplateFormatVersion: "2010-09-09"
Transform: "AWS::Serverless-2016-10-31"

Globals:
  Function:
    Runtime: nodejs8.10
  Api:
    Cors:
      AllowMethods: "'*'"
      AllowHeaders: "'*'"
      AllowOrigin: "'*'"

Resources:
  TestApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: dev
      Auth:
        DefaultAuthorizer: Test-Authorizer
        Authorizers:
          Test-Authorizer:
            FunctionArn: arn:aws:lambda:region:account-number:function:function-name
            Identity:
              Header: Authorizer
              ValidationExpression: ^Bearer [-0-9a-zA-Z\._]*$
              ReauthorizeEvery: 30 # seconds
  Test:
    Type: "AWS::Serverless::Function"
    Properties:
      FunctionName: test-function
      CodeUri: src/test
      Handler: index.test
      Events:
        EndPoint:
          Type: Api
          Properties:
            RestApiId: !Ref TestApi
            Path: /test
            Method: get
            Auth:
              Authorizer: Test-Authorizer

And this where i have very limited knowledge how to achieve this. Any help would be highly appreciated. Thanks

nk2580 commented 5 years ago

I’ve basically done the same but I’ve used swagger to define my api gateway resources.. it’s a little easier than using cloudformation and it reduces the size of your files

mgrissa commented 5 years ago

Could someone please tell me whether configuring a custom authorizer when running locally with SAM Local is supported?

joeythomaschaske commented 5 years ago

@mgrissa I'm running into this issue and I'm assuming running it locally is not supported. It completely bypasses the authorizer and hits your function which is not ideal when your api is user focussed

nk2580 commented 5 years ago

Worse still there’s no way to have the data passed through cognito back to the lambda after authorisation. Basically it’s an unsuitable solution for API security

joeythomaschaske commented 5 years ago

@nk2580 I'm working around it by starting my local api with an environment variable. If I see that environment variable in my lambda then I use the email I pass into the request otherwise I use whats in the event.requestContext.authorizer object

event.requestContext.authorizer is populated by the cognito authorizer when it's deployed and you're calling the api gateway endpoint through a client

mgrissa commented 5 years ago

@joeythomaschaske I would recommend using the Serverless Offline plugin of the Serverless Framework. I am able to run an API Gateway with a Lambda Authorizer and a Lambda Proxy locally thanks to this plugin.

joeythomaschaske commented 5 years ago

@mgrissa I'm not using Serverless Framework. I want to use 1st party tooling if it's available and this is a viable work around.

jqdurham commented 5 years ago

Any update on this? Best I can grok from the docs, this should be working.

MyServerlessAPI:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Dev
      Auth:
        DefaultAuthorizer: VerifyIAMAuthorizer
        Authorizers:
          VerifyIAMAuthorizer:
            FunctionArn: !GetAtt IamAuthorizerFunction.Arn

  IamAuthorizerFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./cmd/authorizer

  TestFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ./cmd/test
      Events:
        APIRequest:
          Type: Api
          Properties:
            RestApiId: !Ref MyServerlessAPI
            Path: /
            Method: get

I'm just looking for the minimal configuration to make it work for PoC.

nk2580 commented 5 years ago

So far the only way I can get the authoriser to take effect is to use the inline swagger feature of SAM. It’s actually not as cumbersome as I originally thought but it does end up making your template.yml file bloated.

jqdurham commented 5 years ago

@nk2580 - Would you mind sharing a working example?

djfabrix commented 5 years ago

Any update on this? When local custom authorizers will be supported?

woozay commented 4 years ago

Ditto ^

bobbyhadz commented 4 years ago

I have a hard time figuring out what doesn't work with sam local, for example custom authorizers, is there any place where one can read the current known limitations?

aaronwhitedev commented 4 years ago

I contacted AWS support (Business support) regarding this issue and received a reply stating that testing the authorizers for API resources locally is not available currently. I was told it's a known issue open with the SAM team and the SAM team will be working on it based on the priority they assigned it - no ETA on when this will be available. I've attached a very slimmed down version of the code to this reply.

aws-sam-authorizer.zip

aodhan-domhnaill commented 4 years ago

So far the only way I can get the authoriser to take effect is to use the inline swagger feature of SAM. It’s actually not as cumbersome as I originally thought but it does end up making your template.yml file bloated.

@nk2580 Hey, do you have an example you'd be open to sharing?

utiliware commented 4 years ago

Here is a patch that supports swagger based Authorizers right now... aws-sam-cli-feature-jwt.patch.txt

destinybonavita commented 4 years ago

Any update on getting this to work locally? I'm surprised after 3 years it wasn't implemented

LennyDuan commented 4 years ago

SAM doesn't support custom authorizers when you run locally now (SAM version 0.48). So you can't test it by using "sam local start-api".

But it supports in SAM YAML template and you can use SAM CLI to build and deploy custom authorizers to you AWS cloud Api Gateway. It can work well if your YAML setup is correct.

rikenppatel commented 4 years ago

As per @joeythomaschaske recommended, you can make it work by running Serverless Offline plugin using Serverless Framework.

I am able to run API GW with Lamda Custom Authorizer locally.

shane-js commented 4 years ago

The current highly decorated response to this open feature request from 2017 is unrelated and clearly states it does not work locally.

Can an AWS employee please comment on the main obstacles faced in implementing this core functionality? Perhaps if more was shared besides that it is not currently supported we could get the community to help with a PR.

Relying on a 3rd party's framework to be able to test code locally is surprising. As it stands now, if one is using a Lambda Custom Authorizer (I'd venture to guess a large portion of the community does) any local test of anything related to authorization is impossible using AWS provided tools to my knowledge. The current "in ecosystem" solution is to deploy to a dev/test environment which massively slows down the development process.


Edit 1: A quick idea:

Perhaps a simpler local implementation could rely on a local json file with mocked policy objects passed to start-api command? Maybe something like --policy-ref (similar to how --env-vars functions). Then perhaps a header can be passed with requests to the local header with a reference to which policy document/mocked authorizer response the _request_handler should "authorize" against?

While this won't imitate the actual flow (in fact is not hitting the custom authorizer at all) it at least allows app specific logic to be tested locally during development.


Edit 2:

If we do as proposed in Edit 1 above perhaps the underlying implementation could rely on the main AWS CLI's test-invoke-authorizer (https://awscli.amazonaws.com/v2/documentation/api/latest/reference/apigateway/test-invoke-authorizer.html)?

In this way two sets of similar code will not need to be maintained and a real authorizer can be hit and passed to the context in _request_handler pre-lambda call.

iaforek commented 3 years ago

opened this issue on 24 Sep 2017

3 years and stil no luck.... no comment.

nk2580 commented 3 years ago

FWIW I ended up moving all my compute to GCP’s Cloud Run because at least in that product I can test everything locally with extreme ease.

aodhan-domhnaill commented 3 years ago

@nk2580 Can you point me to GCP documentation for how to test REST API's locally? I'm curious

nk2580 commented 3 years ago

@aidan-plenert-macdonald Cloud run is a serverless docker container runtime so you test your API locally the same way you would any non serverless application. The only caveat to the platform is that the application needs to be stateless as there’s no guarantee of the same instance serving all HTTP traffic.

To run your application locally you just run ‘docker run’ using the dockerfile for your application

ethanpeterson commented 3 years ago

+1

sunhak-hout commented 3 years ago

I'm new to SAM and I've been stuck for quite a long stressful time testing Authorizer locally and finally found this opened issue. Surprisingly, this has been opened since 2017 and there is no even work-around solution? 😳

nk2580 commented 3 years ago

The only workaround is to deploy and test in a dev account