dherault / serverless-offline

Emulate AWS λ and API Gateway locally when developing your Serverless project
MIT License
5.2k stars 794 forks source link

authorizer function support #41

Closed aliatsis closed 8 years ago

aliatsis commented 8 years ago

It would be great to add support for customer authorizer functions so I can simulate authorization/authentication locally.

dherault commented 8 years ago

Please PR ^^ Thanks!

johncmckim commented 8 years ago

Has anyone started work on this? I'd be happy to contribute a pull request.

Just going over what needs to be done (looking at src). For each endpoint that had a authorizationType or CUSTOM, a hapi auth strategy would be set on the route. The strategy would be linked to the hapi auth scheme that exists for the authorizerFunction (if none exists it would be created).

The authorization function takes event and context parameters. The event parameter needs authorizationToken property set, but I can't find clear docs on what all the properties are. The context argument can be created with createLambdaContext.

Am I missing anything?

aliatsis commented 8 years ago

@johncmckim I was planning to at some point, but had to de-prioritize it for now.

Here's an example: https://aws.amazon.com/blogs/compute/introducing-custom-authorizers-in-amazon-api-gateway/

the event object:

{
    "type":"TOKEN",
    "authorizationToken":"<Incoming bearer token>",
    "methodArn":"arn:aws:execute-api:<Region id>:<Account id>:<API id>/<Stage>/<Method>/<Resource path>"
}

I'm not exactly sure how serverless determines the authorizationToken. The API Gateway docs allow this to be configurable (e.g. method.request.header.Authorization).

The only other requirement I can think of is to return an AuthPolicy with the principalId (which really is the lambda functions responsibility but could be nice to have some validation).

johncmckim commented 8 years ago

Thanks @aliatsis. I'll see if I get some time to put something together this week.

johncmckim commented 8 years ago

Also, what's the easiest way to test this? Can this be run as a CLI from outside a serverless project?

dherault commented 8 years ago

Testing this project has always been a pain. I tried several times to write tests but its intrication with serverless raises testing issues. If you find something better than calling functions for the browser/terminal/postman please tell me!

johncmckim commented 8 years ago

@dherault I have been using API Easy to test the endpoints on my serverless project. See this test file.

If you were willing to have a test serverless project in this repo, you could have a script to do the npm linking, run the sls offline start --debugOffline command and then run vows index.js to test all the endpoints in that test project. This is an idea for another issue, but if you like it I'm happy to create another issue and work on a pull request for it.

johncmckim commented 8 years ago

How far should this go in validating the result of the Authorization function? So far with the pull request I've created it check checks for err and data. Is it worth going into checking wether or not the endpoint matches the resources specified in the policy document?

The methodArn on the event object requires a <Account id> and <API id> (API Gateway ID). Any idea of ways to deal with those? Will dummy values be enough?

For my purposes right now, I don't need to validate the policy document or use the Account ID and API ID. Though I'm not doing anything complicated. @aliatsis what do you think?

aliatsis commented 8 years ago

@johncmckim I think validating that the principalId exists is worth it. Validating the policy would definitely be nice, but probably not required.

dherault commented 8 years ago

Thanks to @johncmckim, please try the new v2.5.0 :)

johncmckim commented 8 years ago

@dherault with some more testing I've realised there's a few differences between how this work and how API Gateway works. I'll send a PR with fixes soon.

dherault commented 8 years ago

@johncmckim Ok sure np

johncmckim commented 8 years ago

I haven't had a chance to do it yet, I'll try soon. But the main issue is that the plugin splits the Authorization header when it should pass in the whole header.

The other issue is that it returns 403. However, it should return 401 when context.fail("Unauthorized") is used and 403 when the policy denies the resource.

edclement commented 8 years ago

It seems that the implementation for custom authorizer support cannot handle promises being returned. Returning a promise works when deployed but the offline plugin expects an immediate value with a principalId.

johncmckim commented 8 years ago

@edclement I've just added support for promises, along with some other fixes.

dherault commented 8 years ago

Merged! v2.5.1

shineli1984 commented 8 years ago

Hi @johncmckim , great work here. Thank you for the efforts to make this work. I can't seem to use environment variables set in s-function.json for authorization function. I had a look at the code. I think the auth function is invoked before any environment variables is set on process.env. Is this the case? or Am I missing anything?

aliatsis commented 8 years ago

@johncmckim I just tried out the authorizer flow and it works but I can't seem to get the principalId in the handler. I have a request mapping set up for $context.authorizer.principalId is this supported? How have you successfully retrieved the principalId from with the handler lambda?

johncmckim commented 8 years ago

@shineli1984 the env variables are something I missed. The file that would need to be updated would be createAuthScheme.js and it would need to populate the env variables as in index.js line 353. Abstracting that could be a good idea.

@aliatsis I missed that property on the context, it doesn't seem to be in the docs. It shouldn't be too hard to add. The principalId is set on the request object, request.auth.credentials.user (auth and credentials may be null). You would then need to update createLambdaContext.js to include the principalId on the context if it existed.

I'm not sure I'll have time this week to add PR's for those issues this week. If you need them soon maybe you could write a PR yourselves? Otherwise, let me know and I can try do it on the weekend.

shineli1984 commented 8 years ago

@johncmckim I'll try to do a PR myself.

shineli1984 commented 8 years ago

@aliatsis A workaround for you I think would be setting PRINCIPAL_ID in your auth function in dev environment. For example:

const decoded = verify(
      last(event.authorizationToken.split(' ')),
      JWT_SECRET
    );
    // this line below is for development env to put user id into event for lambda function invoked afterwards
process.env.PRINCIPAL_ID = decoded.user_id;
dherault commented 8 years ago

@shineli1984 I merged your PR, see version 2.5.2!

dherault commented 8 years ago

Guys thank you all for your good work. There are still a few bugs in the plugin (see other issues), I intend to solve them all in August, unless you do it first of course. Thanks again!