Skip to content

[Feature Request] ALB Custom Scopes #78

@andyfase

Description

@andyfase

issue / feature request

I have a need to perform fine-grained permission checking on a Lambda function behind a ALB. I have extended lambdarest to allow for the existing Scopes functionality to also verify scopes passed from an ALB as it currently does from API Gateway. I would like to provide this code into the core code base - hence wanting to ensure you would be happy to accept it before I raise a PR.

Unlike custom authorizers in API GW, ALB's do not provide a "scope" attribute or claim that is standardized in the event object - I have allowed the developer to specify the "scope" claim that is used (in the call to create_lambda_handler) and then have added code to extract and verify the scopes from the x-amzn-oidc-data header when the request is parsed.

Parsing the ALB header requires the use of several external libraries (base64, jwt, requests) as the header data needs to be decoded, the signature checked (hence the ALB public key fetched). Hence this is not just an addition of a few lines of Python but requires the base dependency to be extended.

I think this could be useful to the wider users of this library - but wanted ton confirm before raising a PR

Thanks!

Example Helper Class to verify ALB Scopes

class ALBScopes:
  def __init__(self, scope_param):
    self.scope_param = scope_param
    self.alb_public_keys={}

  def __get_public_key(self, kid):
    url = 'https://public-keys.auth.elb.' + \
      os.environ['AWS_REGION'] + '.amazonaws.com/' + kid
    req = requests.get(url)
    return req.text

  def __verify_oidc_data(self, data):
    jwt_headers = data.split('.')[0]
    decoded_jwt_headers = base64.b64decode(jwt_headers)
    decoded_json = json.loads(decoded_jwt_headers)
    key_id = decoded_json['kid']
    if decoded_json['kid'] not in self.alb_public_keys:
      self.alb_public_keys[key_id] = self.__get_public_key(key_id)
    return jwt.decode(data, self.alb_public_keys[key_id], algorithms=['ES256'])

  def get_scopes(self, event):
    encoded_data = event.get('headers', {}).get('x-amzn-oidc-data', None)
    data = self.__verify_oidc_data(encoded_data)
    if isinstance(data.get(self.scope_param, None), list):
      return data.get(self.scope_param, None)
    return []

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions