Closed granicz closed 1 year ago
@granicz I have created a proposal for authentication change #1338
About this ticket I just have one thing to change: all RPC calls should be POST by default to not break HTTP semantics. GETs should be non-side-effecting, which we can't know from just the RPC method not having arguments. We can add a way to annotate method to be a "GET", for example adding an argument to attribute: [<Remote(isGet = true)>]
.
We could also allow changing path on the Remote
attribute itself for ese of use: `[<Remote("/myApi/doStuff")>].
This looks very interesting. I have written some code to use ASP.Net's endpoint routing in a WebSharper app and handling RPCs is sub optimal due to the fact the RPC call is not posted to a predictable location. I think this change would allow me to have much finer grained control on RPC access using only the Authorize annotation, looking forward to it!
@rbauduin Just making sure things fall into the right place, can you add an example here?
@granicz Currently, I can annotate WS Endpoints like this:
[<EndPoint "/admin";Authorize(Policy="Admin");Wildcard>] Admin of args:list<string>
This requires a user to have admin rights to access this endpoint.
I imagine something similar could be done for Rpc. Just making this up, but if I could then do something like
[<Rpc "/path/to/call";Authorize(Policy="SuperAdmin")]
and update the AspNet endpoint routing to require the policy "SuperAdmin" for all (POST) requests to "/path/to/call".
As WS Endpoints are defined in a DU type, it's easy to retrieve and handle all the cases. The trick will be to retrieve all Rpc annotations and add the required authorization checks for the requests at these locations. I don't know yet how to do it, and hope it would be possible with reflection without too much fuss. Let me know if you need more info regarding my use case.
Currently, you can define an RPC function in one of two ways:
1) Placing it in a module and marking it with the
Rpc
/Remote
attribute:2) Defining a class with instance members to expose them as RPs. For instance:
In either case, RPs are exposed at the root of the web application and they require a custom communication protocol to call - which the generated client-side code automatically provides (designed to make calls more secure and type-safe.) This special handling includes a custom request header (
x-websharper-rpc
), a hash of the RP that includes "version info" to make sure there is no API mismatch, arguments serialized to JSON, and upon return, activating the response as a JS value.However, this is sub-optimal from several angles, including:
1) Non-WS generated clients (say, ordinary JS code) have a hard time calling these RPs, as the communication protocol is nearly impossible to mimic manually. 2) RPC responses are "asymmetrical" compared to other client-side values, they contain additional metadata that only makes sense to a WebSharper client. 3) The location of the RP is not reflected in the URL, but in the request header - which is counter-intuitive from a service-oriented viewpoint.
Therefore, this ticket is a proposal to change the current RPC communication protocol to one, where:
1) There is no metadata in responses (see #930 #922 ). 2) RPs are exposed at a predictable location, which can also be customized easily, for instance
/{module-name}/{function-name}
/{type-name}/{function-name}
. 3) RPs that have no arguments (their signature isunit -> ....
) are mapped to simple GET endpoints, and all others use POST to send their arguments via a single JSON payload (can be extended later). 4) No custom request headers are necessary.This ticket should be coupled with #793 to maximize utility.