User being faster than Cloud Function - android

I have a Cloud Function which is creating a user profile in Firestore, right after a user signs up with FirebaseAuth (with E-Mail & Password).
A user document could look like this
users/{<uid>}
{
username:"smith",
status: "I love pineapple pizza",
email:"smith#mail.com"
}
After signing up, I'm giving my users the chance to change their status attribute. The problem is that the user can be faster than the triggered cloud function. This leads to the problem that the user tries to change a document which is not existing.
How can I make sure that the cloud function is already triggered ?
The only thing which is on my mind is checking from the client side if the document exists (maybe a loop). Which is imo not a really good approach.

You could attach a listener to the document. With that your onSnapshot should fire when the document is created, at which point you can update it.
Depending on the exact writes the client and server need to do, also consider making the writes idempotent. So: ensure that whoever goes first, all writes succeed.

Checking to see if the document already exists is your only approach. You really don't have any guarantees about how exactly the client and server will interact with each other. If you need to perform some action on the client, it will have to coordinate with the server, with no assumptions about when exactly a Cloud Function may trigger.
If you want some client code to trigger when a function creates a document in Firestore, just set up a listener on that document (don't use get() - it should be a listener instead). The listener will get triggered when the document is created, and you will have assurance that the function is complete

Related

Prevent users from manipulating firestore data

I have an android app which uses firebase for authentication and firestore for storing user data. Once the authentication is complete for a first time user, we collect extra info like nick name, age etc and store them in Firestore. We also have an important field called USER_BALANCE. This is set to 0 on new account creation. How can i make sure they dont manipulate this field.
int USER_BALANCE = 0;
User user = new User(name, email, USER_BALANCE,0,0, refreshedToken); db.collection(FIREBASE_COLLECTION_USERS).document(firebaseUser.getUid()).set(user).addOnSuccessListener(this);
We also have certain task in app,on completion where user gets rewarded and points will be added to USER_BALANCE in firestore. I want to make sure nobody decompile the app and can update the field with whatever value they want.
You can use firebase rules for that. Firebase rules check if a user is able to manipulate your data. You can choose for the user to be able to only read the specific value. Because you haven't provided a view of your database structure I haven't tell you how the specific rule that you need will be framed. Check this blog it really helped me a lot when I was starting.
---Edit---
The below firebase rule checks if the user tries to update the specific field and blocks it. You can check this post for more information.
function notUpdating(field) {
return !(field in request.resource.data)
|| resource.data[field] == request.resource.data[field]
}
match /Users/{userId}{
allow read;
allow update: notUpdating('user_balance');
}
Anybody can decompile the app and attempt to make changes. There is no way to prevent that. They don't even need your app to write the database, since it has a public REST API.
What you will need to do instead is use Firebase Authentication along with security rules to determine who can read and write which documents. There is no other way to control access to your database if you intend for your app to be able to read and write it directly. If you can certainly disable public access entirely and create your own backend - but you have just shifted the responsibility onto the backend for making sure the API it exposes also does not allow arbitrary callers to make unauthorized changes.

Firebase user A transfer child content to user B. Possible?

Users gets to sign up/log in via email/password authentification on firebase. Done. I am also able to get the current user and also have him write personal info in his child also, game score points are updated to firebase as he progresses. Done.
Now, say I want user A to transfer points to user B. Knowing that user A is the current user. And perhaps user A knows user B's email or userID. How can I make him access/write in user B score points.
I have tried a few methods none worked:
I tried FirebaseDatabase.getInstance().get reference().child("profile/").child(X.getText().toString()).addListenerForSingleValueEvent blah blah blah.
Where X is B's user ID. Well this returned null.
I read about firebase-admin userRecord ..and after trying to put it in my Gradle and running into many errors I discovered it was for Server side. So not applicable.
I read about making a list of all users but dat didn't seem cool since what happens if I have a million users. It shows all when A only needs B's.
Oh. I'm a mechanical engineer. So please don't refer me to read firebase docs and etc . Most of the terms just confuses me and don't tag this as already asked question believe me I have read them all. Also, I'd very much love it if you could be kind enough to either refer me to a tutorial or explain the codes you have written using A and B as case study.
Here's an answer at kind of a high level:
I would suggest adding a node that will notify other users of pending transfers. Something like
transfers
transfer_0
fromUid: "uid_a"
toUid: "uid_b"
amt: "100"
transfer_1
fromUid: "uid_c"
toUid: "uid_d"
amt: "10"
All users should add an observer query to that node to watch for any newly added nodes where toUid equals their Uid. In this case childAdded for uid_b.
When user a transfers 100 to user b, it will be written to that node and user b will be notified the transfer node was added and can update their own node, then remove transfer_0.
Likewise, if they are offline and then log in, the childAdded will pull in any new transfers so then their node can be updated and the transfers removed when complete.

Is FirebaseAuth # signInWithCredential result internally cached (for some period) or it performs a network trip & reauthetnicates on each invocation?

The previous version of Firebase (before google-fication) had an API Firebase#getAuth() which would return non-null if there was a currently logged in user allowing us to short-circuit authentication. That was spelled out in the docs and clients were encouraged to use it for performance.
Is the new FirebaseAuth#signInWithCredential API intended to not perform a full re-authentication if the user has recently authenticated, or clients of the library need to keep track of that themselves and only invoke when necessary? I couldn't find any details in the official docs or in the guides.
Basically, what is the contract of the method? Is it designed for use where I can invoke it multiple times and it returns immediately the successful authenticated state after a first full authentication (up to some expiry period), or does it perform a full-blown network-based authentication every single time?
If you want to know if a user is signed in, call FirebaseAuth.getCurrentUser(). It is the equivalent of Firebase.getAuth() in the 2.x SDK.
Calling FirebaseAuth.signInWithCredential() will try to sign in with the credentials you specify.
If you want to know when a user gets signed in/signed out or something changes about their state, use FirebaseAuth.addAuthStateListener().

Firebase: how to update data only if missing or different? (eg User registration)

This is a pretty basic question and I'm surprised that the Firebase guides don't cover it. In their own example of registering a user, the code seems very inefficient unless there is some built-in optimization I'm unaware of. It seems each time you log in, you will also push the user data to firebase.
ref.authWithPassword("jenny#example.com", "correcthorsebatterystaple",
new Firebase.AuthResultHandler() {
#Override
public void onAuthenticated(AuthData authData) {
// Authentication just completed successfully :)
// irrelevant: some code to construct userData
ref.child("users").child(authData.getUid()).setValue(userData);
}
#Override
public void onAuthenticationError(FirebaseError error) {
// Something went wrong :(
}
});
1) There appear to be no checks if a user already exists at that location and if the data we are attempting to persist is different. How would one accomplish that? Would you need to first read the fields, check if different and then attempt to update (all of this in a transaction)?
2) If I'm right about (1), would you recommend storing some indicative data locally to short-circuit the need to "check" with firebase if the user is registered? I could store a boolean + a local copy of the user so I can (A) check the registered boolean and if true, then (B) check if the local user data is the same. If both are true, I can completely skip step (1) above.
Obviously I want to avoid any caching logic in app space due to the usual complexity cost. Does Firebase guarantee super fast local-only queries (what I'd do in (1)) if the data hasn't been changed? I want to avoid extra client logic as much as possible.
Thank you.
You may overestimate how often authWithPassword() runs. In a well-implemented Firebase app, you will use an onAuth() listener to detect when a user has previously signed in. So you'll only need to actively authenticate them when their session has expired or otherwise has become invalid.
If you'd prefer to only write the user's profile data when they first sign-up, you can do the same logic when you call ref.createUser().
To know client-side whether the data is the same, you'd indeed first have to read it. Which negates any positive effect you might gain by not writing it.

User Session Management in RoR API for Android

I am building an API in RoR to be used by an android app. I have looked at various other similar questions on SO but found nothing which fits my exact use case. I need to identify a logged in user and respond to the request accordingly. This goes just beyond getting the user id to user categories, implicit preferences (different from settings) to give the user a more personalized experience.
In a web app this is done through a session cookie using which I can essentially call the following methods:
current_user.id # Gets user's id
current_user.category # Gets user's category
current_user.auth_level #Gets user's permission level
To do this, in the webapp I have the following setup:
In login action:
...
#user_session = session_setter(email, password)
...
def session_setter(email,password)
#session = UserSession.new(:email => email, :password => password)
#session.save
end
UserSession.rb
class UserSession < Authlogic::Session::Base
logout_on_timeout true
last_request_at_threshold 100
skip_callback :persist, :persist_by_cookie
skip_callback :after_save, :save_cookie
skip_callback :after_destroy, :destroy_cookie
end
session_store.rb (configured for memcached)
require 'action_dispatch/middleware/session/dalli_store'
App::Application.config.session_store :dalli_store, :memcache_server => ['127.0.0.1:11211'], :key => '_session'
This setup allows me to access current_user object once the user is logged in and access all columns in the table users.
For android, this is what I have figured out:
Create an api_key for each user who creates an account. This could have an expiry date. Maintain and store this key in the users table. The api_key is passed on to the app on the first request where it is stored in something like sharedPreferences. Every request should carry the api_key to the server. Put filters on actions which require the user to be logged in, to check if the key is present or not.
Now, here is my question: In this setup I would need to retrieve the users record from the table (or store it in the memcached with the api_key as a key value pair) on every request through the filter.Is there a way to use something like current_user with the api?
In short I want to to replicate the same strategy for maintaining logged in users for the api as I have for the web app.
API is supposed to be unaware of your web-app contexts.
API is the classical stateless HTTP.
You ask it a question, it gives you answers. Albeit, from your web app's state and data, but it has no concern with what goes on at that end.
An API cannot, or rather, should not be coupled with the user logged in/ logged out state.
It is your webapp's responsibility to query the API with as much information it needs to reflect the logged in state.
Eg.,
If your API has a method get_recommendations,
it should ideally take in multiple args, to be able to handle all cases
logged out users, will make the query, wrt the page being viewed. As in, a matrix movie scene being viewed, should give other matrix scenes as reco's
logged in users, will make the query, wrt other aspects. As in, how many action videos has been viewed by this user, can be passed in as an arg.
The API endpoint should have the ability to handle both these scenarios seamlessly.
Make other calls to other methods if need be, to find out the current user's (identified by a user id key passed in) details.
So, from a design perspective, you should not pass in anything other than the fact that this request is for a signed in user with id = X.
Let your API handle everything else.
API is nothing but a special function/method.
Methods can do pretty much anything, as long as they remain true to their description / signature.

Categories

Resources