2
votes

The API Gateway endpoints we are using shall be restricted via permissions to a specific audience.

The idea is to use the lambda authorizer to fetch the permissions from an external service and then create the policy to allow or deny access to the endpoint.

For the matching of the permissions to the API endpoint the endpoint would need to provide the permissions it needs to the authorizer.

My question is now how can I enrich the endpoint data with its own required permissions and use them in the authorizer lambda(probably via the event) for further validation.

Example:

  • User1 is forwarded to the first endpoint GET/petstore/pets(this endpoint needs the permission -> View:Pets)
  • Lambda authorizer requests the user permissions from the external service
  • The service returns: [View:Pets , View:Somethingelse, etc.]
  • The lambda authorizer matches the user permissions against the required endpoint permission and creates the Allow policy on a match
  • User2 does the same but does not have the permission for viewing pets, no match -> Deny

Here is my code for the lambda:

import {Callback, Context} from 'aws-lambda';
import {Authorizer} from './authorizer';

export class App {

    constructor(private authorizer: Authorizer = new Authorizer()) {
    }

    public handleEvent(event, callback: Callback): Promise<void> {
        return this.authorizer.checkAuthorization(event, callback)
            .then((policy) => callback(null, policy))
            .catch((error) => callback(error, null));
    }

}

const app: App = new App();

module.exports.lambda_handler = async (event) => {
    return await app.handleEvent(event);
};

Code for the checkAuthorization method:

export class Authorizer {


    public resourceAuthorizer: ResourceAuthorizer = new ResourceAuthorizer();
    public authenticationChecker: AuthenticationChecker = new AuthenticationChecker();

    public checkAuthorization(event, callback): Promise<object> {

        const endpointPermissions = event.endpointPermissions;  // <== this is what I need, a custom field in the event which
                                                            // is provided from the api endpoint in some way
                                                            // in my example this whould contain a string or json 
                                                            // with 'View:Pets' and 'View:Somethingelse'

        return this.authenticationChecker.check(event)
            .then((decodedJwt) => {
                const principalId: string = decodedJwt.payload.sub;

            return Promise.resolve(decodedJwt)
                .then((jwt) => this.resourceAuthorizer.check(jwt, event.endpointPermissions))
                .then((payload) => callback(null,
                getAuthorizationPolicy(principalId, 'Allow', event.endpointPermissions, payload)))
                .catch((payload) => callback(null,
                getAuthorizationPolicy(principalId, 'Deny', event.endpointPermissions, payload)));
            }).catch((error) => {
                console.log(error);
                callback('Unauthorized');
            });
    }
}

The event.endpointPermissions is basically what I am looking for. Depending on the API endpoint this should be filled with the permissions neccessary for that endpoint. The resourceAuthorizer then fetches the users Permissions from the external service and compares them to the endpointPermissions and then creates the Allow or Deny policies.

So where can I enter the endpointPermissions in my API Endpoint to provide them to the Authorizer?

2
It would help if you had some code (even if it was part pseudo) to demonstrate what you are trying to achieve and what language you are using.K Mo
I updated the question with code snippets.StV

2 Answers

2
votes

The event being passed to the Authorizer contains a methodArn, which is in the format:

arn:aws:execute-api:<Region id>:<Account id>:<API id>/<Stage>/<Method>/<Resource path>

This would give you the Method and Resource Path that you need. It also would give you an identifier of the API, but not the name of the API itself.

The API id, can be used to get the API name by using the AWS SDK. See here.

This should give you everything you need to construct your endpointPermissions value.

0
votes

I got a solution to my problem without having to parse the ARN, but it's pretty unconventional:

  1. In the method request of a resource create URL query string parameters with the permission names and set the checkbox for 'required'

method request URL query string parameters

  1. When the request is called from the client(Postman) these mandatory parameters have to be provided as keys, they are endpoint-specific. The values do not matter because only the keys will be used at evaluation.

postman request parameters

  1. The event received by the authorizer now contains the queryStringParameters which can be evaluated for further use.

    queryStringParameters in cloudwatch