How to Secure Wallet Updates in Firebase (User vs Admin Roles)

Hello Kodular Community,

I need help understanding the correct and secure way to handle wallet updates in a Kodular app using Firebase Realtime Database. I believe my current approach works functionally, but I also understand that all clients are untrusted, so I want to redesign this part properly.

Please assume I am not yet experienced with role-based security, and I want to learn the right architecture.


My Current Situation

  • Backend: Firebase Realtime Database
  • Auth: Firebase Authentication
  • Networking: Web component (REST API)
  • Everything runs from the client side

As of now

  • A user makes a deposit using a payment aggregator API
  • The API responds with success
  • The same user app then updates their own wallet balance in Firebase

This works, but I now realize this is not secure, because,

  • The client can be modified
  • API responses can be replayed or faked
  • Users should not be allowed to update their own wallet

What I Want to Achieve

I want a much more secure model, where,

  1. Users,
  • Can sign up and sign in
  • Can submit a deposit request
  • Can see their wallet balance
  • Cannot update wallet balance
  1. Admin,
  • Uses a separate Admin App (same Firebase project)
  • Reviews pending deposit requests
  • Confirms deposits
  • Updates user wallet balance
  1. Wallet:
  • Is read-only for users
  • Is writable only by admins

What Confuses Me

This is where I’m stuck conceptually,

  • If I set Firebase Rules so that wallet is read-only, then:
    • How does the admin update the wallet?
  • If both apps use the same Firebase project, how does Firebase know who is a user and who is an admin?
  • How do I separate permissions without trusting the client app?

I’ve seen mentions of,

users/{uid}/role = "admin" | "user"

But I don’t understand,

  • How this role is assigned securely
  • How can I add automatically user role when user is signed up
  • How Firebase Rules use this role
  • How admin can write while user cannot

What I Am Looking For

I am asking for,

  1. A simple explanation of how to separate user vs admin roles
  2. The correct data structure for:
  • wallet
  • deposit requests
  1. An example of Firebase Realtime Database rules where,
  • Users submit deposit requests
  • Admin updates wallet
  • Users cannot change wallet or role
  1. Confirmation that this approach works with Kodular Web component

As of now

  • I am not using Cloud Functions for now
  • I understand that hiding URLs or UID is not security
  • My main concern is wallet manipulation
  • This is an MVP, but I want to avoid dangerous mistakes

Summary (My Core Question)

How do I design Firebase rules so that users can deposit and create pending requests, but only admins can update wallet balances, even though both apps use the same Firebase project and REST Web component?

Any guidance, examples, or explanations would be extremely helpful.

1 Like

Hi dear,

I believe everything can be solved by setting the correct rules for your RTDB.

(I should mention that I don’t know if this is the most efficient method, but I used it before in an old project and it works well)
You could create an admins node containing a dictionary of admin UUIDs and set rules that allow only them to modify a specific node, while making it read-only for everyone else.

{
  "rules": {
    "deposits": {
      "$uid": {
        ".read": "auth != null && (auth.uid === $uid || root.child('admins').child(auth.uid).val() === true)",
        ".write": "auth != null && (auth.uid === $uid || root.child('admins').child(auth.uid).val() === true)"
      }
    }
  }
}

With these rules, only an admin with boolean true in “admis” can write in the deposits nodes,
while a logged-in non-admin user can only read their own node.

Firebase structure

{
	"admins": {
		"nvkdd7lD4IQJCvNo9cYyWoDtt8c2": true
	},
	"deposits": {
		"EP6Rmmr874eyv4Ak3RZ7D88XhFh2": {
			"amount": 3456
		},
		"nvkdd7lD4IQJCvNo9cYyWoDtt8c2": {
			"amount": 366436
		}
	}
}

In this case, the user with UUID nvkdd7lD4IQJCvNo9cYyWoDtt8c2 is an admin and can read and write in all deposits nodes.
The user EP6Rmmr874eyv4Ak3RZ7D88XhFh2 can only read from its own node deposits/EP6Rmmr874eyv4Ak3RZ7D88XhFh2.

It’s not everything you asked for, but you can easily adapt it to what you’re trying to do.

Obviously, you’ll need to use an extension or the Web component to handle authenticated requests.

I understand, though am still not getting something at the moment, how must I differentiate user and admin when signing up? Must I stored the UUID’s in different nodes?

Is this structured generated when signing up?

Am using web component, is there any extension or any other way to hide my Firebase database URL? Most when user is performing any activities, eg there’s some tools available out there that can see how the app handle/send out going process and see the real URL.

How do you intend to grant permissions to a user to create an administrator account?

The idea in my old project was that an administrator had to be added manually in the admins section with a UUID = true pair.
You could use Cloud Functions that, by passing a password, automatically insert the UUID into the admins list, but that’s not very secure, it should be a step handled on the backend, not the frontend.

Why do you need to hide it? If your database has solid rules, you could even write it on the station walls :rofl:

That’s where i still thinks its not more secured,

At first, my idea was this,

Let’s assume there are two apps,

  • App A → Client (user) app
  • App B → Admin app

My initial thought was that when a user registers from App A, the app would automatically assign the role user, and when someone registers from App B, the app would automatically assign the role admin. Is this possible?

There some network traffic interceptors these capture all HTTP/HTTPS requests that my app makes. Eg Charles Proxy, Burp Suite, MITMproxy, Wireshark etc,:rofl::rofl::rofl::rofl:

If your concern is security, creating an app that creates admin accounts without any filtering is a problem.

Well, as I told you, if your database has secure rules, there’s nothing to worry about.

Hey RayzZz, I understand that I should do it properly, though you didn’t answer my question

Is this possible to create user roles when user is signed up? That’s where I am stuck and if yes, how?

It really depends on how you want to proceed,whether you want to do it the correct way or if you just need something that works.

You could use my method and, with the admin app, register the new account retrieve the uuid, log in with a service account that is already an admin, write the pair uuid = true, then log in again with the new account, which will now be recognized as an admin.

Realtime Database works through rules or custom tokens, I don’t think it’s possible to distinguish access just via appId, and implementing access with custom tokens makes things quite a bit more complicated.

2 Likes

With the rules set correctly, it won’t be possible for you, since to write you will need to be an admin and when you try to register as an admin, the account will not yet exist.
Ideally, one administrator should be able to register another, so you’ll need at least one admin.

You can expose your URL and API key; that’s not a problem if the rules are correct (a user will need to be authenticated and have a token); otherwise, they won’t be able to do anything.

1 Like

Two methods to “write once”:

  1. Set a rule for the first write that is set to allow a “write” only if the target path does not already exist.

  2. Set application logic to enforce this behavior: check if a value exists before attempting to write it.

1 Like

Hey @Joe90 can you explain more, maybe I didn’t explain well, but at least u get what I want. I have tried different ways but still nothing seems done so far. Maybe i should add them manually.