User Roles / Permissions on Frontend - React / GraphQL / Apollo Client - reactjs

Recently started working with React / Apollo Client / Auth0 / Hasura and have some questions on handling frontend permissions. I'm using Auth0 to handle my authorization on whether a user is logged in and have my backend setup to check as well when handling mutations / queries. My question is now how to handle it on the front end.
A user can create a team that will store the info in my "teams" table and also create a record in my "team_staff" table as either a manager or coach. That was all straight forward. What I'm looking to do now is when a user visits, for example: www.mysite.com/team/update/1 to check if the user exists in the "team_staff" table and if not show them an error message or even a redirect. Also looking to hide certain buttons when viewing a team based on whether they are a staff member or not.
Should I handle this at the login and do a query for all the teams that user is a staff member on and store in a session / cookie or have a query / check inside that component each time it's called? Or am I way off and should do it another way?
Hopefully this question makes sense. Thanks!

This question makes sense, I believe many developers would have some similar problems.
TLDR;
Make API request in componentDidMount to get the right permissions (after signed in of course).
For this question, we have many solutions, and which is the best, depends on your infrastructure, your team and so on. Basically you need to call API to check the permission because your permission stored in the backend. Never store permission on the frontend storage like session, cookie, etc.
I can give some approaches.
First, call API right after signed in to get permission information, for example:
Get list of permitted routes, then, whenever user browse to a specific route, check to make sure that route in list of permitted routes.
Get list of permitted team like array of team ids, then in each route, get team id, check if that that team exist in above list.
But I'm sure you will realize they're almost the same, just different the data you get and how to process them. And two solutions totally depends on you.
All API request should be placed in componentDidMount of page component, because you will want to make sure the permission should be applied correctly as soon as the backend has changes.
Happy coding!

Related

Protecting Firestore without requiring authentication

So currently in the project we have a collection of documents that don't require authentication to be read. They are write/update protected, but everyone can read.
What we are trying to prevent is that someone looks at the firebase endpoints and somehow manages to scrape the entire collection in json format (if this is even possible). The data is public, but I want it only to be accessible from our website.
One of the solutions we could think of was SSR (we are already using Next.js), but implementing SSR just for this reason doesn't seem very enticing.
Any suggestions would be appreciated.
EDIT:
Let me rephrase a little bit.
From what you see in the network tab, is it possible to forge/create a request to Firestore and get the entire collection instead of just the 1 document that was intended?
The best solution in your case is SSR. I know, it could sound as not enticing, but well, let's reason on when we should use SSR, then. In your use case, there is an important requirement: security. I think this is already a strong enough reason to justify the usage of SSR.
Also, creating an ad hoc service account for the next.js app, and securing the data with custom rules that allow the read of your data only to that service account, would only improve the overall security level.
Last: reading the data server side should make your site work a little faster, even if it would be difficult to notice, because we are talking about milliseconds. Notice that your page, as it is now, will need to be loaded, before the request to Firebase could be sent. This is adding a small delay. If the data is loaded server side, the delay is not added.
is it possible to forge/create a request to Firestore and get the entire collection instead of just the 1 document that was intended?
If you want to limit what people can request from a collection, you're looking for security rules. The most common model there is some form of ownership-based access control or role-based access control, but both of those require some way of identifying the user. This could be anonymously (so without them entering credentials), but it'd still be a form of auth.
If you don't want to do that, you can still control how much data can be gotten through the API in one go. For example, if you in the security rules allow get but not list, the user can only request a document once they know its ID. Even if you allow list, you can control in rules what queries are allowed.
I think one approach could be writing a Cloud Function that retrieves this public data using the admin SDK. Then, you could set a rule that nobody can read those documents. This means that only your Cloud Function with the admin SDK will have access to those documents.
Finally, you could set up AppCheck for that specific Cloud Function, this way, you ensure that the request is coming from your client app only.
https://firebase.google.com/docs/app-check

Best practices for similar RBAC schemas?

I all, I'm writing a boilerplate for future projects. Composition is as follows:
Server:
Express,
Prisma 2,
Typescript,
JWT Auth (Access token in memory, Refresh in cookie)
MySQL
I'm writing an RBAC schema, and have successfully written express middlewares to determine if a user is logged in, and for if a user has a specific permission on their role.
If you've ever used any of the minecraft server permission plugins, I'm trying to emulate the common pattern used there.
Users have role(s)
Roles have permissions
Roles can inherit permissions from one or more roles
Roles have a "nextRole" field to determine what role to give when the "promote" event is triggered.
Everything works fine on the server side.
What I'm wondering about is, how should I go about copying the middlewares (login, permissions) to the client side, and how should I determine whether a user has permission to do something?
What I've looked at:
Creating a "hasPermission" endpoint wouldn't be very good as I'd need to make an API call every time a permission check is needed.
Eager loading all roles and permissions from the api when logging in and returning them in the response (I can't eager load the recursive role inheritance/nextRole as far as I know)
Returning ONLY the user without roles and permissions for the JWT/login bit and getting roles/permissions from their own endpoints (again, needs to be recursive to get all inheritance and said permissions from inheritance)
Has anyone created an RBAC schema like this, and how did you go about checking permissions on the client side without being too redundant/using too much memory/too many api calls?
This is a good question, here's my answer to it.
An app is normally protected by the auth info, which means it could be blocked if a user is not permitted. If this is a server application, it can be easily done, because the session can be used to find out the current user info including roles.
However if this is a client app, it's a bit tricky. Say we can protect a route (page or section of page) once the user log in.
if (!user.authenticated) return null
We can use the above line to block private or public user. Or other information you can grab from the user to protect more.
if (user.role !== 'Admin') return null
We could wrap in these into a component, such as
<Allow role="admin" render={...} />
I believe you get the point. However there's something which is very unique about the client approach. The entire user info is returned back, and only the user info, not the user type or permission type.
So to follow your plan, do we need to share a permission or role type to the client side? This is a million dollar question.
In practice, the UI never needs the complete info, why? because UI normally reshapes the permission a bit. That doesn't mean you can't share the complete info from the backend. Doing that may make the UI job easy or more complicated. Nobody knows.
The reason is what I explained above, the UI is writing a if statement (could be hidden) anyway. Either this if is true or false, most of the front-end code is already loaded. It's very different than the backend version, which can entirely block the deliver of the content.

How to have a safe admin handling with reactjs and firebase

I would like to add functionalities depending on whether or not the user logged in is the administrator but I don't really know which condition (for conditional rendering with delete buttons etc) I should use to check if the user is the admin or not. Is it safe to do it based on the id of the user ? In the first place, I thought about testing the user in every component I want him to have functionalities, with a state called "user" using recoiljs to get access to the user in the whole app but I'm afraid people could change the state with the react tool extension and then pretend they are the admin and so delete articles and stuff... What's the best way to test if a user is the admin or not using firebase authentification in a react project ?
It's never safe for client code to assume admin responsibilities without absolute enforcement from your backend. It's unsafe because client code can be compromised and might not work the way you expect. And it's running on a device that the user controls fully.
Client code can check some indicator to see if the user is admin (in whatever way you find suitable), but the final check needs to happen on your backend, either through security rules (if you're using Firebase products like Realtime Database, Firestore, or Cloud Storage), or in code running on a secure backend, including products like Cloud Functions.

Can the client modify react component state?

I'm building an admin page for an application and have a state value 'authenticated' that flips from 'false' to 'true' after a successful login (which is authenticated on the server) which then shows the actual admin panel.
Are component state values safe from tampering by the client? Basically, if the client can modify my 'authenticated' state value to 'true', they can skip the login and go straight to the admin panel (which I obviously don't want).
I read that React Dev Tools allows the client to modify values yet everyone says "validate on the server" but I am validating on the server and updating my state accordingly, if the user is approved. If it is not wise to have a state value manage this, what is the right way to conditionally show the admin page after a successful, server-side authenticated login?
I think this is an important question since tampering with state values in a React app can have huge negative consequences on data integrity within an app/database.
TL;DR: Either require an authentication token with every request or require authentication through a session.
Never trust users always. One potentially big issue is if you "hide" admin actions behind the admins page without requiring authentication.
For example, assume the backend server uses a REST API to accept commands. In the admin panel you get links to administrative actions like a button 'Delete Everything' that sends a DELETE request to server.net:8080/api/admin/everything without requiring any authentication. If you're a user, you can find that in the code potentially and then send a DELETE request to that address from anywhere without any repercussions.
We'd never give administrative privileges to anyone who would want to delete everything... Because we'll never untrust someone. Right?
Worse, someone might find the server and fuzz some inputs to it, and oops! They manage to delete everything (or even worse, GET everything stored in the database). This wouldn't be hard to do, especially if the server you use to authenticate is the same server you use to issue commands. History has proven "security through obscurity" to be a very bad paradigm. Every action should be authenticated, even if it seems like the actions will be hard to find.
Generally, providing a JSON web token or some other form of authentication token and having the user send that with every request is a good start at least, especially if it has an expiration date. The token would be provided through a separate request with valid credentials.
Sending a token with every single request obviously isn't ideal. There are a couple of other things to try. For servers using PHP, you can probably trust sessions (though very many people who know more than me would probably disagree). In more modern cases, you could try to use Web Sockets, requiring the token after connection. Then only after authentication with the token do you allow the user to make administrative requests.
That way, even if a user knows the exact command they can send to perform any action, the server won't let them without a current session or token. Unfortunately, unless you're already using Web Sockets or depending on a session, it will likely require a lot of changes. I'd consider this to be critical though.
It is always possible to tamper values in the front-end, there is no way you can rely solely on the front end to ensure security.
Your best approach is to implement some form of authentication and authorization on your backend. In this way, even is some users pretend to be admin, they will be blocked when you do the next request to the server.
Perhaps if you can send more information regarding your problem, we can think of a more specific solution.

Identifying non-logged in users in a stateless RESTful API

I'm developing a stateless RESTful API which will be consumed by an iOS app and an AngularJS browser app. In this API, auth tokens are required for any actions relating specifically to an authenticated user (adding new content, editing details etc).
Now, my application also requires non-authenticated users to be able to add items to their shopping carts. This is where I'm unsure. Since the application is stateless and therefore has no sessions - I'm not sure how to identify the user if they haven't already logged in and been given an access token.
One solution I'm considering is generating some other lower class of token that will identify this non-logged-in user. Then I can send this with every request to fetch and modify the cart.
The issue described here is not really that you need a token, but that you need a way to uniquely identify specific shopping carts.
My assumption based on the question is that there is some generic /cart endpoint, that has a different content based on who's interacting with it. This design is a bit problematic (as you're seeing) as it might be better to just have a unique shopping cart uri per user.
/cart/[some-unique-id]
However, even in this scenario you might still want to use some form of authentication to identify that someone who's modifying the cart is still allowed to.
If you're using OAuth2, the easiest would be to just create a new grant type. OAuth2 is extensible, so you could definitely add some 'anonymous' grant type that doesn't require any info, but just provides a bearer token.
In this case your server would always have to make sure that the token is valid, but also make sure that it still correctly disallows accessing the endpoints that require 'real authentication'.

Resources