PhilanthropyDataCommons / service

A project for collecting and serving public information associated with grant applications
GNU Affero General Public License v3.0
8 stars 2 forks source link

Demonstrate how a user will filter to their own data #939

Closed reefdog closed 6 months ago

reefdog commented 6 months ago

The API will soon allow filtering data by creator with the createdBy={userId} param. However, currently, the front-end has no way of knowing user IDs.

While there are several theoretical interfaces (like a front-end user admin) where these may need to be discoverable, the only current need is for admins — who can see more than just "their stuff" — to filter views to only "their stuff". E.g., letting admins switch between viewing "all proposals" and only "my proposals", or, hard-coding a front-end filter on the Bulk Uploads page so that admins only see their own uploads.

The front-end will need something like an endpoint to retrieve the current user's ID, or, a keyword param (or magic createdBy value) that proxies to createdBy={my user ID}, or something.

Opening this as a discussion! If there's already a solution or existing task, feel free to close.

slifty commented 6 months ago

@reefdog what do you think of a new API endpoint of GET /me or GET /users/me or /users?me=true which would return the user object associated with you? You could call this once as part of the auth flow and then cache the result on the front end.

Some other ideas that I had were:

  1. ?createdBy=me being special -- I don't love this because the type of createdBy is currently a number, and although some day I'd like to make references more fancy I don't LOVE shaving that yak right now, and it would increase complexity.
  2. ?createdBy=0 being special / referring to you -- I don't love this either since it doesn't feel super intuitive (why would 0 mean me?)
  3. ?createdBy={sub of your user} which I'd love to support at some point, but right now we don't treat any of our object reference filters as full blown complex references, and this is not a yak I think we have time to shave in this phase.
  4. a special /bulkUploads/mine endpoint or some such thing -- similar to /users/me but we'd have to do it for EVERY ownable entity which feels like an O(n) complexity solution when /users/me would just be that single endpoint.

If you think that /me or /users/me or /users?me=true would be a reasonable API design I think it could solve the problem / not force API design changes elsewhere, and plus you might want to know that object for other reasons (especially if the object gets more complex some day)


If we think that's weird, the least possible awkward implementation would be:

I'm updating my preference to this, and might actually go implement it since as an endpoint it makes sense even outside of this use case. We could still make a bespoke / weird / not-quite-restful /users/me shorthand URL if we really want to.

reefdog commented 6 months ago

In terms of API design, I agree your preference (I'll call it option 5) is the cleanest. (Side note, a nice thing about /users/me is that it's basically unhackable, short of the same JWT manipulation any call is vulnerable to. Is /users?authenticationId={sub} any less secure for some reason? I'm not sure whether sub is the equivalent of a public key, or is partially-decoded token data or what.)

I do have concerns about implementation cost to the front end. We'll have to peel back our auth library to figure out how to make a dependent external/API call during the login flow. For the standard flow (click "Log in", redirect to Keycloak, redirect back to standard landing spot) it's no problem.

But for the "try to access a protected resource, redirect to Keycloak, redirect back to protected resource" flow it's trickier, or at least may be our own yak shave. That redirect behavior comes for free from the library. Will we be able to insert a dependent API call (and response handler) into the process in a way that still redirects to the requested resource at the end (and handles errors from that API call)?

So of course I have a selfish preference for the magic of options 1 or 3, which doesn't require me to do any preflight. (I agree 2 is weird. 1 and 3 require API consumer training too, but their magic is substantially more intuitive.)

That said, I know the API needs to be designed for more than just the front end's narrow concerns. If option 5 is very fast for you to implement, then we can at least give it a go. I just think we — the front-end — should time-box using it, before asking you to SHAVE THAT YAK.

What do you think?

slifty commented 6 months ago

I believe this is now done (from a backend PoV) -- there is more to do overall (e.g. add /users/me) but I think this issue is done in that it's now possible to get your user object using /users.