4 ms·
Curious what you mean by the API flexibility Vs security point. To me, the API for a SPA is just another view over the exact same data that would be accessible
by davnicwil 6y ago
Curious what you mean by the API flexibility Vs security point. To me, the API for a SPA is just another view over the exact same data that would be accessible in the HTML output of a server render (which, indeed, is the 4th point in the article). Your access to that data is gated by a cookie with a token in it in both cases.
On the code level, it's really not all that different - the operations you perform 'directly' through function calls in a pure server render model you simply put behind another HTTP interface.
I get that this mapping work might be a bit cumbersome and take some extra work, but I'm unconvinced that it introduces some kind of security tradeoff - the client ultimately has the ability to call through to exactly the same operations, just getting the data back in a different format, right?
- tdeck 6y agoBy security I mean pretty basic things like "client should only be able to query objects the user account has access to" and "client should only be able to perform certain kinds of transactions and mutations". On the back end that's not too tedious - just make sure I only query for what I want. But with a SPA I can't just assume the client will make only the "right" queries, otherwise I could just expose a SQL query endpoint to the client and never have to think about APIs again. Instead I have to figure out what data access patterns my frontend will need and implement those, along with whatever visibility restrictions are required. I'm forced to do the former in the backend (as well as the frontend) because the latter can only be done in the backend. (Aside: I realize that back in the day people did indeed use database mechanisms like DB users and stored procedures to do what I describe, and then client apps connected directly to the database. But this practice seems to have faxed and isn't readily doable on the web anyway).
- davnicwil 6y ago> "client should only be able to query objects the user account has access to" > "client should only be able to perform certain kinds of transactions and mutations" The solution to these is equal for server rendered HTML containing {data} or an API just getting {data} in raw form - the request contains an access token which identifies and authorises the user - same goes for mutations on {data}. The answer to the question 'how do you know who the user is when they request a server rendered page containing some sensitive data?' is literally the copy/paste solution to how to do authentication on your API - as in, it's basically the same code that must be implemented on the backend in either case :-) The point is it's the same data and mutations, just a different interface over them. There is no security tradeoff of using one over the other.
- abraae 6y ago> it's basically the same code that must be implemented on the backend in either case :-) No, it's not. If I expose an API e.g GET /widgets,I need to ensure that there is no combination of parameters that will ever return a widget that the user should not see (e.g because it belongs to some other department). Whereas when I render a web page server side, I just need to write a single, correct, SQL statement that pulls in the widgets that this specific web page requires.
- davnicwil 6y agoI respectfully disagree: it's the same code, moved around a bit of course, but literally the same logic making whatever conditional statements or database calls necessary to do permissions :-) It's the same data, with the same auth needed. If it's accessible to anyone over the network you need auth for everyone in either model. The auth code is the same code in either model, only the interface changes. Looking at your example to illustrate: you have user A and user B, who have permission to see different types of widgets depending on what department they belong to. -- Scenario 1: filtering per-resource There is a server-rendered /widgets page that lists all widgets, filtering based on what types of widgets the requesting user can see. API implementation: /widgets endpoint that returns a list of all widgets for the client to render. It must filter based on what type of widgets the requesting user can see, so that only widgets they can see are included in the list. Same logic as above. -- Scenario 2: grouping resources and authenticating at entry There are different server rendered pages for each department, listing all widgets for that department: /widgets/foo-dept, /widgets/bar-dept. You must check the permissions on the requesting user before rendering the page, to see if they have access. If user A can't see foo widgets, you give them some kind of permissions error when they request the /widgets/foo-dept page. API implementation: Add a dept param to the /widgets endpoint that lists only widgets in the specified department. If dept param is populated, check that the requesting user has permissions to see widgets in that department. If they do, proceed, if not, throw a permissions error and deal with it on the client to produce the exact same UI as in the server rendered example. Same logic as above. -- If you have resources accessible over the network, and you do auth on them based on user attributes, you have to do that auth either way, regardless of if you're server rendering the data into HTML pages or returning it as JSON or whatever else and rendering the HTML on the client.
- indigo945 6y ago> (Aside: I realize that back in the day people did indeed > use database mechanisms like DB users and stored > procedures to do what I describe, and then client apps > connected directly to the database. But this practice > seems to have faxed and isn't readily doable on the web > anyway). This is actually exactly what postgREST does for you. So in that case, you actually can eliminate the backend entirely and run the entire application in the frontend, with the security provided by stored procedures and row-level security. I think Firebase, which is somewhat more widely used, also enables a similar architecture. That said, I actually agree with your preference for server-side rendered applications. The problem is that from the browser, you can't really do anything that is not SQL -- you can't, for example, call into a C library, query a legacy API, and so on. Of course, all of that can be solved by deploying more microservices, but that not only means you have to think about API design again, it also increases operational costs. Unless you need lots of fancy live-updating things, server-side rendering is still fine, and even when you do need to update dynamically, there's Blazor Server now.