roadrunner-server / roadrunner

🤯 High-performance PHP application server, process manager written in Go and powered with plugins
https://docs.roadrunner.dev
MIT License
7.91k stars 413 forks source link

[💡FEATURE REQUEST]: Authorization plugin [JWT] #912

Open OO00O0O opened 3 years ago

OO00O0O commented 3 years ago

Describe the solution you'd like For example: EMQX has multiple authorisation plugins - http, mysql, redis etc. Considering that a lot of people use RR for microservices with JWT, authorization could be moved to it too.

Example config

auth:
    config: // this could be for each auth method, there for you can have multiple auth methods at once.
        header: Authorization
        query: token
        body: token
        timeout: 180 // ttl for cached authorization
    jwt: // does check jwt, append decoded info to attributes
        config:
            public: ...
    worker: ~ // send auth request over worker like Broadcast plugin does. Everything that workers returns is appended to user "session" and that data is appended to attributes in next request
    http: ~ // append headers etc and send further and wait for 2** response
    kv: ~ // key found and append its content to attributes
    sql:
        config:
            table: token
    passwd: ~
    list: ~ // basic auth
        config:
            hash: md5
        users:
            - username: SomeUser
              password: 5f4dcc3b5aa765d61d8327deb882cf99

For now only JWT makes sense. Example in practice: https://docs.emqx.io/en/broker/v4.3/advanced/auth.html#password-salting-rules-and-hash-methods

rustatian commented 3 years ago

Hey @OO00O0O , thanks for the FR. I'm not quite sure, that I correctly understand your proposal. Could you please provide more details, especially about how other plugins will collaborate with the auth plugin and what data they should send to it? And full configuration, w/o ~. For example, what should be done for the http plugin (in terms of authorization), if the auth plugin exists?

OO00O0O commented 3 years ago

View of this functionality really depends on how you see RR in future and now. Me personally see it as http/ws application server/framework. I do not care about GRPC or Temporal. So I do not consider them in this example.

auth: // maybe this could be middleware for http
   config: // here we tell RR what http query/header(maybe body) parameters to use
       header: Authorization
       query: token
       authenticators: [jwt-auth, worker-auth] // executes in order
    jwt-auth:
        type: jwt
        public: ...
        fail: ignore
    worker-auth:
        type: worker
        ttl: 3600
        fail: deny

Flow: Client first time access service with expired JWT token in header Authorization

  1. RR sees used authorization header and takes its value
  2. Because in auth.config.authenticators jwt-auth is first, it sends value to it.
  3. jwt-auth sees that it's invalid query and there for returns false
  4. Because auth.jwt-auth.fail == ignore it ignores failure and send further
  5. worker-auth uses same token to reauthorze. If it succeed it cashes return value with timeout
  6. Now it attaches object to each http requests attributes.

Main idea is, that RR should handle generic cases, so: authorization, cache, database, websockets, etc. AND only send work to workers that actually should be done. I would even consider templating engine, where php worker responds with object.

rustatian commented 3 years ago

Ok, I got your idea. Generally, handling bad or expired JWT's on the server side might be a good idea (to free up workers' resources and pass only valid headers). But... ...to get good feedback from the other users, would be a great practice to make a detailed FR, especially, if you want a new plugin. From your proposal, I see only a JWT sample, it's fine. Imagine If I start working on your proposal w/o any information about other details. It would be a mess. So, make a detailed proposal, including all keys you think should be in the configuration. I'm not asking, but, would be nice if you provide some diagrams about the flow. Make a detailed explanation about every config key, you want to include. Then we will wait for other users' feedback and if your proposal passes the review, I'll be happy to implement this.

OO00O0O commented 3 years ago

Sorry, but I think this is discussion first - implementation later. Maybe it's not worth your a time. We should wait for more input from community or spiral dev team.

rustatian commented 3 years ago

np, I respect your time as much as I respect mine 😃

spiral dev team

np, I'll ask PHP teammates at tomorrow daily :) Please, keep in mind, a good discussion is only possible with a detailed proposal :) People should clearly understand, what do mean by your proposal and then productively discuss it :) Also, keep in mind, that I'm not a PHP dev at all, so, I have to understand, how do you want to use this feature and how this feature will be used by the community :P

OO00O0O commented 3 years ago

Main problem for me is that I do not know what PHP devs prefer - voters or roles. Be more implicit or explicit. Therefor it's hard to write good proposal that would pass.

I personally prefer configuring generic functionality and writing specific code. Others will do configurations and abstractions till death. And some people can write http servers almost in one line when they need them and ignore configurations at all.

rustatian commented 3 years ago

I personally prefer configuring generic functionality and writing specific code. Others will do configurations and abstractions till death. And some people can write http servers almost in one line when they need them and ignore configurations at all.

In your proposal, you have to cover all these options.