[Feature Request] ALB Custom Scopes
andyfase opened this issue · 2 comments
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 []
@andyfase i think it makes sense but i dont completely comprehend it yet. Would you be up for a google meet call or something as i think it would be quicker to pass an understanding in that medium.
a second thing:
increasing lines of code by using stdlib (urllib etc) could maybe reduce the list of dependencies (requests can be dropped atleast) decreasing the build size