Open fubar opened 6 years ago
Hey @fubar,
sure, here is the example:
# serverless.yml
custom:
eventTypes:
user.created:
authorizer: authFunc
functions:
hello:
handler: handler.hello
events:
- eventgateway:
type: sync
eventType: user.created
path: /hello
method: GET
authFunc:
handler: handler.auth
Closing for now. Let me know if you have more questions.
Thanks @mthenw, that is helpful! Am I understanding it right that authorizers can only be associated with events? If I wanted to call a function based on an HTTP request with authentication as well as based on a non-HTTP event without any authentication, I would need two different event types? I initially expected authorizers to be specified in the function definition alongside eventType
as to allow for easy composition.
I took a deeper dive into this today and tried out a few things, and would like to share a few observations. Now I am aware that this project is still in its early stages and under active development, but I did not want to miss the chance to point out these things and get your thoughts and help.
serverless gateway emit --event "test.foo" --data "{}"
).
On top of that, it looks like the first deploy of an authorizer (when it's new or after I've changed its name) causes the function to execute successfully, as if no authorizer was specified at all. Subsequent deploys cause the 403 behaviour described above.eventType: http.request
. This means that an authorizer (once working) needs to be set on the generic http.request
event type, which in turn means that either all of my API endpoints have to use the same type of authentication, or I need to make the authorizer aware of which endpoint is called and use different logic accordingly, which defeats the purpose of the original intention to specify different authorizers for different events/endpoints. This is somewhat related to my initial expectation that authorizers would be linked to endpoint definitions, and not to event types.type: sync
or type: async
: Event emission returns immediately in both cases, with the same response payload, whether I emit the event from command line or in code through the SDK. It would be incredibly useful if I could fire events in code and wait for the response. It did however affect the parameters that need to be passed to the SDK method:
When using async
, this works:
eventGateway
.emit({
event: "test.foo",
data: { foo: "bar" }
})
When using sync
, the above throws an error. It requires parameters like specified at https://github.com/serverless/event-gateway-sdk#emit:
eventGateway
.emit({
eventType: "test.foo",
eventTypeVersion: "1.0",
source: "/foo/bar",
contentType: "application/json",
data: {
foo: "bar"
}
})
I'd be happy to help debug further.
Also, when are you planning on publishing a stable release version?
Thanks!
@fubar thanks for your feedback.
ad. 1. I will look into that and let you know
ad. 2. Specifying authorizer on event type is the first step. We are aware of limitations that you mentioned. We are thinking about allowing defining authorizers either on endpoints (method + paths) or on subscription level.
ad. 3. For custom events EG should behave differently depending on type of subscription. For async subscription it should return 202 Accepted
for sync subscriptions it should return response from subscribed functions. I will investigate this weird behavior that you mentioned.
@fubar Thanks for the detailed feedback. This is really helpful.
I did some digging today and found some of the root causes of your issues.
1) Most importantly, the Framework plugin wasn't registering functions in the Event Gateway that didn't have an eventgateway
event attached to them. This meant the authFunc
wasn't registered in the Event Gateway. There's an issue tracking this bug, and it should be fixed soon.
Semi-related, there's an [issue](https://github.com/serverless/event-gateway-sdk/issues/52) for the Event Gateway SDK that will allow users to specify a `path` and headers for an `emit()` request. This will be helpful as subscriptions are set up on paths, and headers may be the mechanism for authorization.
2) You're right on the authorization for http.request
. Our initial goal for authorizers was to set them up for Event Types and solve the asynchronous / pub-sub auth issue. It didn't make sense for each downstream subscriber to implement auth for an asynchronous event that's emitted, so it need to be on the Event Type.
We'll still working on the mechanisms for allowing auth on `http.request`. As Maciej noted, it will likely be on the subscription.
3) Still getting to the last issue around sync
and async
for emitting. Will follow up when I know more.
Thanks again, and feel free to reach out if you have more questions.
Thanks for the responses @alexdebrie and @mthenw and for addressing the issues. Looking forward to further updates!
@fubar I tested the sync / async emit behavior with the event-gateway-sdk
, and I was able to get it to work.
I set up two subscriptions -- an async
one for user.deleted
, and a sync
one for user.created
. Then I ran this script:
// Construct your client
const SDK = require('@serverless/event-gateway-sdk');
const eventGateway = new SDK({
url: process.env.EVENT_GATEWAY_APP_URL
})
// Test the async subscription
eventGateway.emit({
eventType: 'user.deleted',
eventTypeVersion: '1.0',
cloudEventsVersion: '0.1',
eventID: 'e58961a5-f53b-4849-8ae5-cb06c031919e',
source: '/services/users',
contentType: 'application/json',
data: {
userID: 123
}
}).then(res => console.log(res.status))
// Test the sync subscription
eventGateway.emit({
eventID: '1',
eventType: 'user.created',
cloudEventsVersion: '0.1',
source: '/services/users',
contentType: 'application/json',
data: {
userID: 'foo'
}
}).then(res => res.json())
.then(json => console.log(json))
The first one returns immediately with a 202 status code and an empty body. The second one returns with a 200 status code and the response from my Lambda function.
In the collapsed sections below, I have my serverless.yml
and handler.py
if you'd like to reproduce. The synchronous user.created
also has an authorizer on it to show how to get that to work. You might need to use the master branch of the serverless-event-gateway-plugin
as I've fixed a few bugs but haven't released a new version yet.
Let me know if that solves your problem! I'll keep this ticket open until you give the sign off 👌.
@alexdebrie I can confirm the above example works for a local Event Gateway instance (latest master) with Serverless version 1.29.2 and serverless-event-gateway-plugin version 0.7.8, but not for the platform version (app and tenant specified in the serverless.yaml file), where I get the "Failed to update user.created event type due the error: Authorizer function doesn't exists." error.
Edit
I get the same error on my local EG instance.
Looks like the eg serverless plugin tries to update the user.created
event type before the authorizer function gets registered
Hi, Is there any update on the issue? As per what @baniol reported, I'm similarly getting `Authorizer function doesn't exists." errors. Thanks.
Hey @johnlim, sorry to hear that. I just tested a few times and it worked for me.
Can you share the following things:
serverless-event-gateway-plugin
you're using.serverless.yml
you're trying to deploy.Thanks!
Hi @alexdebrie thanks for looking into this.
My Environment Information -----------------------------
OS: darwin
Node Version: 8.11.3
Serverless Version: 1.30.3
--------serverless-event-gateway-plugin
"version": "0.7.8"
# -------------serverless.yml
functions:
createUser:
handler: handler.createUser
events:
- eventgateway:
type: sync
eventType: http.request
path: /users
method: POST
userCreated:
handler: handler.userCreated
events:
- eventgateway:
type: async
event: USER_CREATED
authorizerFunc:
handler: handler.authorizerFunc
custom:
eventTypes:
http.request:
USER_CREATED:
authorizer: authorizerFunc
My current use case though is to do auth on http.request
. Is there any update on this as well? Thanks!
Hi @alexdebrie, were you able to duplicate this? Do let me know if you need any further info. Cheers.
Hey @johnlim, I think I found the issue. I was using an older version of the plugin.
I've got a pull request open for it in the plugin repo here. Can you test and let me know if it solves your problem?
Thanks for your patience with this!
@alexdebrie Thanks for the fix! I've verified that it fixes the aforementioned issue. Really appreciate it
@alexdebrie If I understand correctly, the current design for authorizers on http requests require them to be tied to http:request
event types? For example:
custom:
eventTypes:
http.request:
authorizer: authorizerFunc
USER_CREATED:
If so, how would I declare a public http endpoint if I also need to declare some private ones (i.e with authorizer functions)?
We'll still working on the mechanisms for allowing auth on http.request. As Maciej noted, it will likely be on the subscription.
Any update on this? Thanks.
We'll still working on the mechanisms for allowing auth on http.request. As Maciej noted, it will likely be on the subscription.
@alexdebrie Are there use cases for multiple subscribers to a single http event? If not, instead of supporting authorizers on subscribers of http.request
event types, why not simply have users implement http endpoints via the "traditional" serverless way like so
functions:
main:
handler: handler.main
events:
- http:
path: /
method: GET
authorizer: auth
and have the http endpoints emit events, if needed. Not only does this save development effort but also makes it easy to setup custom domains for the endpoints.
That does however bring up a big security question. Based on my understanding, it seems that anyone who knows your Event Gateway url, will be able to emit events. For example, by using the event-gateway-sdk
, one can simply input the tenant url and call emit(). i.e hackers could start injecting events into your system. The workaround would be to require all events to implement authorizers but seems to a high overhead. Is that the intention/recommendation?
@alexdebrie @mthenw Just following up to see if you have any advice for me for the aforementioned issue. Thanks in advance.
Hey @johnlim, great question.
While the Event Gateway does allow for REST-like HTTP endpoints, it's really intended to be an event router more than a traditional API Gateway. As such, it's not as full-featured in the API Gateway department, such as allowing for granular authorization strategies on different endpoints. We may add this functionality in the future but are presently focused on more event-driven flows.
Does that help?
Hi @alexdebrie, Thanks for getting back. So if I understand correctly, 1) In the meantime, for http endpoints that need granular control, it's recommended that we use the traditional serverless/API gateway approach instead of event-gateway REST-like HTTP endpoints? 2) As event-gateway events can be triggered/emitted by external systems (as long as they know the event-gateway tenant url), all subscribers must/should implement authorizers to validate if they should execute? Thanks in advance.
Hey guys,
we're using the
"@serverless/serverless-event-gateway-plugin": "0.7.8"
version in one of our projects however the issue mentioned here: https://github.com/serverless/event-gateway/issues/478#issuecomment-410555962
still persists.
Has anyone else experiences the same problem since the merge to master for 0.7.8?
I'm in the process of evaluating this project for our API. It looks promising, but one core aspect is unclear to me.
Could you provide an example of how I would specify/reference an authorizer function in
serverless.yml
?Thanks!