powersync-ja/powersync.dart

Multi user issue

JCKodel opened this issue · 3 comments

Scenario:

I have an app where some tables have shared data (no auth necessary for read) and some are user-dependent.

On the server, I'm using Hasura, and the insert, update, select and delete permissions are bound to my JWT token (actually, Firebase JWT token), so the user can only insert, update, read or delete its own data.

Problem:

bill.gates@microsoft.com logs in. Then, he goes offline. Then, he saves or updates some data. Then, he goes offline. Then, he signs out.

Some hours later, steve.jobs.zumbi@apple.com enables the wi-fi and logs in. It makes some changes and then, boom: PowerSync sync no longer works.

Why?

Because PowerSyncBackendConnector gets Zombie Steve Jobs auth token (because it is the user authenticated at the moment), but, when I ask PowerSync to connect(connector: MyVersionOfPowerSyncBackendConnector), uploadData(PowerSyncDatabase database) is called, but db.getCrudBatch() now contains data from both Bill Gates and Zombie Steve Jobs.

The problem is: I cannot save that data to a secure server (such as Hasura with row permission by user id), because I'm trying to send data from Bill Gates, but my token is for Zombie Steve Jobs, and the whole mutation fails.

Worst yet: there is no half-batch.complete(): either all succeeds or all fail.

Solution:

PowerSyncBackendConnector.uploadData should have some configuration while calling db.getCrudBatch(), (same as sync_rules.yaml) so db.getCrudBatch() can fetch only the data that is possible to write at this moment (where user_id is s.jobs.zombie.69).

Hi @JCKodel,

(whoops, accidentally closed the issue - keyboard shortcuts)

Have you considered DB-level filtering?

  • conceptually, use a separate SQLite DB per user
  • append the user ID from the JWT to the DB file "path" argument in the constructor
  • getCrudBatch() will then be guaranteed to only have entries for the currently logged in user

Not too clued up on your use case, so curious if this could work for you!

-Kobie

  • conceptually, use a separate SQLite DB per user

Yes, I'm implementing that right now, but then I would duplicate any public tables (the ones that are synced without user info). Also, I could only write to the database once I have a user id, which can be challenging in some scenarios.

Not perfect, but acceptable.

If the storage size of duplication becomes an issue, have you considered having a shared SQLite database that can be accessed by any user? Basically you'd have N+1 sqlite databases where N is the number of users on the device and the +1 is the shared database.

It's possible to have multiple connections open in the same app at the same time.

You could potentially also do your "userless writes" to this DB?