svdgraaf / serverless-basic-authentication

Use Basic Authentication using Api Gateway api-keys
MIT License
22 stars 19 forks source link

Can't access the API with Basic Auth #14

Open armonge opened 5 years ago

armonge commented 5 years ago

I'm trying to use the plugin for some simple auth in a quick flask project. However for some reason i can't get the basic auth to work. For now it's only working when using the x-api-key header

Interestingly seems like there is a change when using a wrong password, meaning the plugin is indeed checking for the password

$ http -a key:bad-password https://something.execute-api.eu-west-1.amazonaws.com/dev/
HTTP/1.1 401 Unauthorized
Connection: keep-alive
Content-Length: 26
Content-Type: application/json
Date: Mon, 24 Jun 2019 15:26:04 GMT
Via: 1.1 5f3006c64f23c42b9bf4b3b63c77aedc.cloudfront.net (CloudFront)
WWW-Authenticate: Basic
X-Amz-Cf-Id: pdkyegI5p9uwYUsNcx7hcByQzz3tIybsEf4cjpAnny7X4f3y2X_A9g==
X-Amz-Cf-Pop: MUC50-C1
X-Cache: Error from cloudfront
x-amz-apigw-id: bym-AHkIjoEFv6A=
x-amzn-ErrorType: UnauthorizedException
x-amzn-RequestId: 6196325d-9694-11e9-9f9e-4bcc800f8014

{
    "message": "Unauthorized"
}

In the case i use a correct password the response changes from "Unauthorized" to "Forbidden"

$ http -a key:good-pass https://something.execute-api.eu-west-1.amazonaws.com/dev/
HTTP/1.1 403 Forbidden
Connection: keep-alive
Content-Length: 23
Content-Type: application/json
Date: Mon, 24 Jun 2019 15:19:28 GMT
Via: 1.1 af3abf09293a5c762de5e451f8d6a913.cloudfront.net (CloudFront)
X-Amz-Cf-Id: f2ytwy0NhZCNu8e8crNouNO8DXK5gmioaaU90NexMvhdyaJUV0taIQ==
X-Amz-Cf-Pop: MUC50-C1
X-Cache: Error from cloudfront
x-amz-apigw-id: bymABEVgjoEF1PA=
x-amzn-ErrorType: ForbiddenException
x-amzn-RequestId: 7524ddb0-9693-11e9-9253-1d415f8ab2fb

{
    "message": "Forbidden"
}

This are the logs i'm getting from the basic_auth lambda

Authentication response: {'principalId': 'my-key-name', 'usageIdentifierKey': 'the value', 'policyDocument': {'Version': '2012-10-17', 'Statement': [{'Action': 'execute-api:Invoke', 'Effect': 'Allow', 'Resource': 'arn:aws:execute-api:eu-west-1:redacted/dev/*'}]}}

And this is the result i get when using x-api-key

$ http https://something.execute-api.eu-west-1.amazonaws.com/dev/ x-api-key:good-pass
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 6374
Content-Type: text/html; charset=utf-8
Date: Mon, 24 Jun 2019 15:26:45 GMT
Via: 1.1 5b3be43b5ff3292b36e9c737ff94254a.cloudfront.net (CloudFront)
X-Amz-Cf-Id: a2xuZm9BKXzmw7mF7fnKTl-KuhthNeA2S6mlpX6cn1Wxof8MUuO4oA==
X-Amz-Cf-Pop: MUC50-C1
X-Amzn-Trace-Id: Root=1-5d10ebb5-92e29a8008a9968073257520;Sampled=0
X-Cache: Miss from cloudfront
x-amz-apigw-id: bynEXELRDoEFnGw=
x-amzn-Remapped-Content-Length: 6374
x-amzn-RequestId: 79d996d0-9694-11e9-95ca-7d989a5d9004

With the corresponding logs:

Authentication response: {'principalId': 'token', 'usageIdentifierKey': 'the value', 'policyDocument': {'Version': '2012-10-17', 'Statement': [{'Action': 'execute-api:Invoke', 'Effect': 'Allow', 'Resource': 'arn:aws:execute-api:eu-west-1:redacted/dev/*'}]}}

The only difference i can find is the principalId value

For reference here's my serverless.yml

service: some-project

provider:
  name: aws
  iamRoleStatements:
    - Effect: Allow
      Action:
        - apigateway:GET
      Resource: "*"
  apiKeys:
    - key

plugins:
  - serverless-wsgi
  - serverless-python-requirements
  - serverless-basic-authentication
  - serverless-apigw-binary

custom:
  wsgi:
    app: app.app
    packRequirements: false

  apigwBinary:
    types:
      - 'multipart/form-data'

functions:
  app:
    handler: wsgi_handler.handler
    runtime: python3.7
    events:
      - http:
          path: /
          method: ANY
          private: true
      - http: 
          path: '{proxy+}'
          method: ANY
          private: true

resources:
  Resources:
    GatewayResponse:
      Type: 'AWS::ApiGateway::GatewayResponse'
      Properties:
        ResponseParameters:
          gatewayresponse.header.WWW-Authenticate: "'Basic'"
        ResponseType: UNAUTHORIZED
        RestApiId:
          Ref: 'ApiGatewayRestApi'
        StatusCode: '401'
svdgraaf commented 5 years ago

afaik, the response for the password authorizer is correct, it should return the token id as principal id...

svdgraaf commented 5 years ago

Sorry, I meant usageIdentifierKey. That is used to identify which token is used:

https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html

The output is actually correct, the fact that you get a Forbidden instead of Unauthorized means that api gateway actually accepted the response as well...

You can check if this is not a problem with your backend through the "Test" feature in api gateway, there you can see if the problem is still there when access the backend directly, as this bypasses the custom authorizer.

armonge commented 5 years ago

Interestingly enough, the API gateway and the application work correctly (minus auth of course) when i remove the serverless-basic-authentication plugin. I also tested using the "Test" feature and serverless-basic-authentication enabled, the API Gateway response is the one i'm expecting from my backend

Also, even with serverless-basic-authentication enabled the backend does produce the expected result when using the x-api-key header.

http https://something.execute-api.eu-west-1.amazonaws.com/dev/ x-api-key:good-pass

The issue only happens with 'Basic Auth'

rilian commented 5 years ago

i am having similar issue. with plugin attached, all existing endpoints that have private: true in function definition return {message: null} after 30 seconds, and all inexisting endpoints return this interesting message:

{"message":"Authorization header requires 'Credential' parameter. 
Authorization header requires 'Signature' parameter. 
Authorization header requires 'SignedHeaders' parameter. 
Authorization header requires existence of either 
a 'X-Amz-Date' or a 'Date' header. 
Authorization=Basic Zm9vYmFyOg=="}

i have decrypted this base64 string Zm9vYmFyOg== and it is foobar: - i definitely have no such string in my codebase.