Firebase client side UID and Firebase function UID do not match - ios

I am trying to use Stripe with Firebase and have followed along the linked Firestripe example project. When a user signs up for the app, the following function is triggered in Firebase which creates a Customer object in Stripe.
exports.createStripeCustomer = functions.auth.user().onCreate(event => {
const data = event.data;
return stripe.customers.create({
}).then(customer => {
return admin.database().ref(`/stripe_customers/${data.uid}/customer_id`).set(customer.id);
});
});
The last line in the function above is supposed to write the Stripe customer ID as a child of the Firebase UID in the database which it does successfully.
Now on the client side (iOS - Swift) when trying to write to the same authenticated user by obtaining their UID using the following:
let userID = Auth.auth().currentUser!.uid
Database.database().reference(fromURL: "https://REDACTED.firebaseio.com/").child("stripe_customers").child(uid).child("sources").setValue([
"token": token.tokenId,
])
it creates a whole different child under /stripe_customers/ meaning the UIDs don't match...
How do I go on obtaining the real UID of the user from the client side? Or am I doing something wrong? Any help or insight would be appreciated.

What you're doing in the first code sample is storing data under Stripe customer ID while in your second code sample, you're attempting to retrieve data based on your Firebase UID.
What you want to do is store user objects named as your Firebase UID, then store your Stripe ID in a child object. Finally, query for your Firebase UID object not its Stripe ID child.
Furthermore, I can't see you using your let userID = Auth.auth().currentUser!.uid
anywhere in your query. If you declare a constant that you're not using for your example, it's better to leave it out.
Let me know if you need more help.
Also, have a look at Firebase's new Cloud Firestore.
It's still in beta, but it's extremely powerful compared to Firebase Realtime Database.

Related

Swift and Firestore security rules, firebase security

I'm new developer working on my first Firestore app. I've changed the rules on Firestore to make the data more secure for user, but it's not allowing read/write.
This is the key line and I don't know how to configure it specific to my app -
match /some_collection/{userId}/{documents=**} {
I don't know if I change the "some_collection" to my collection name or if some_collection in that sense is an actual wildcard type of parameter itself.
Also, do I need to pass in the userID somehow from my swift application to Firestore? where is userID coming from in this line? I'd prefer to make the rule such that only the user who created the data can read/write. I believe this block is to allow any authenticated user, so I'm just trying to explore each step.
service cloud.firestore {
match /databases/{database}/documents {
// Allow only authenticated content owners access
match /some_collection/{userId}/{documents=**} {
allow read, write: if request.auth != null && request.auth.uid == userId
}
}
}
Addressing your questions:
This is the key line and I don't know how to configure it specific to my app.
match /some_collection/{userId}/{documents=**}
I don't know if I change the "some_collection" to my collection name or if some_collection in that sense is an actual wildcard type of parameter itself.
In the line above "some_collection" is not a firestore wildcard and you need to replace some_collection with the actual value of your collection.
Also, do I need to pass in the userID somehow from my swift application to Firestore?
Yes and it is expected that before reading or writing to/from firestore:
You had already created and configured the firebase object.
firebase.initializeApp({
apiKey: '### FIREBASE API KEY ###',
authDomain: '### FIREBASE AUTH DOMAIN ###',
projectId: '### CLOUD FIRESTORE PROJECT ID ###'
});
You had already authenticated your users with firebase auth.
firebase.auth().signInWithCustomToken(token)
.then((user) => {
// Signed in
// ...
})
.catch((error) => {
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
Passing the userId is done by the firebase object when you call db. collection(“col123”).add or any other method. If you look at how firestore is initialized:
var db = firebase.firestore();
You will see its dependency with the firebase object.
where is userID coming from in this line?
The userID is coming from the firebase object.
I believe this block is to allow any authenticated user, so I'm just trying to explore each step.
Yes, the last rules allow any authenticated user to read and write from/to the subcollections/documents wildcard {userId}.
Lastly it is also expected that there is some naming consistency in the ids of your firestore documents or subcollections.
This means when you create firestore documents, use the firebase.auth.uid as the document id.
Otherwise, the rule from above will fail because the value behind {userId} is not equal to firebase.auth.uid of the logged user.
To achieve the latter, you can refer to this answer.
I highly recommend you have a look at this video(from the firebase channel) since it elaborates more on the core concepts of firestore security rules.
I hope you find this useful.

observeSingleEvent(of:with:) fetching old data

Background:
I have an app where new users complete a sign-up form (consisting of a username, email, and a password) to register for an account. When the new user submits the form, the app checks its Firebase Database to see if the username has already been taken. The problem here is that observeSingleEvent(of:with:) is not getting the newest data. When I update the data directly from the Firebase console, the changes are not reflected back in the returned results of observeSingleEvent(of:with:). Even between app restarts, the new data changes are not returned.
The only thing I've seen on the issue is here. A user says not to have persistence enabled when using observeSingleEvent(of:with:). But, I have persistence disabled. Also, I set the keepSynced() on the FIRDatabase instance to false (and I've tried true as well). No luck with either setting.
Here's an example of what I am trying:
let usersDatabaseReference = FIRDatabase.database().reference().child("users")
usersDatabaseReference.keepSynced(false)
// Query the database to see if the username is available
// The user with the username "mark" was removed from the database via the Firebase console
let username = "mark" // This user used to exist in the database (and was previously added through the app).
usersDatabaseReference.child(username).observeSingleEvent(of: .value, with: { (snapshot) in ... } )
The above code block should return the newest data (one where the user "mark" should not exist).
I am wondering if anyone else ran into this problem, or if anyone has possible solutions.
Note: Developing using iOS 10.1 and swift 3.
the observeSingleEvent(of:with:) return the value only one time
try this :
refHandle = postRef.observe(FIRDataEventType.value, with: { (snapshot) in
let postDict = snapshot.value as? [String : AnyObject] ?? [:]
// ...
})
this will add a listener on database event link
I recommend you refactor your JSON data model using the username as key. Doing this, you should get an error when trying to create a duplicate user in the database.
So, my problem had nothing to do with caching or fetching old results. Some of my code logic depended on checking the value of the snapshot parameter that was passed back in the completion handler for observeSingleEvent(of:with:). I wrongly assumed that I could check for a null database value by comparing it with nil; this led to a misinterpretation of results.
The best way to actually check for a null database fetch is by calling the exists() method on a FIRDataSnapshot instance. exists() returns true if the snapshot contains a non-null value.

Can't get list of users [duplicate]

This question already has answers here:
How do I return a list of users if I use the Firebase simple username & password authentication
(7 answers)
Closed 6 years ago.
I want to show list of users in my app. I use default Auth system from firebase. But response always empty.
FIRDatabase.database().reference().child("users").queryOrdered(byChild: "email").observe(.value, with: { snapshot in
print(snapshot)
})
But snapshot is always Snap (users) <null>
The Firebase Authentication system does not automatically insert anything into the Firebase Database. I guess your database is empty, which is the reason for snapshot being null.
Your code looks correct, but as I said there might not be any data in your database to receive.
Depending on what exactly you want to achieve you should consider storing user meta data in you database. A good point to do so would be directly after the user creation.
Registering a user with Firebase Authentication, by default, does nothing to modify your Firebase Database. Authentication and Database are two pretty much unrelated services. It's a common practice, once you register a user, to save an entry in your Database with their uid, so you can relate the two services:
let auth: FIRAuth? = FIRAuth.auth() // The authentication object
auth?.createUser(withEmail: email, password: password) { (user, error) in
// If registration was successful, `user` is a FIRUser with a uid
if let userId = user?.uid {
let exampleDBPath = FIRDatabase.database().child("users").child(userId)
// Write the user object, for instance a user name or other data, to this path
exampleDBPath.setValue(someJSONAboutTheUser) { (error, result) in
// Now you have a spot to modify your user in the database
}
}
}
This FIRUser created from registration is the same type of object you'll get when a user tries to sign in, so you can find the correct user in the database via the same uid.

How to get Firebase User (fb auth) directly? (without realtime db ref) [duplicate]

This question already has answers here:
How do I return a list of users if I use the Firebase simple username & password authentication
(7 answers)
Closed 6 years ago.
I'm making an application with a Message object which has sender and receiver properties.
I'd like to assign these properties Users that live in the Auth' tab of Firebase Console (fb auth-ed).
Since my application's functionality only needs to read data from the users, I thought I don't need to add them to the realtime database, just access them through FIRAuth (which I'm assuming is the internal representation of the Auth' tab on Firebase Console).
I can get the 'current user' like this:
FIRAuth.auth()?.currentUser
And this will be the 'sender' property of a Message.
But for the 'receiver' property I want to grab a User (that the sender will choose from a drop down of fb contacts) from the fb auth-ed ones listed in the Auth' tab of Firebase Console. For recipient, I need to be able to do something like this (pseudocode):
users.find(where uid == current_user.uid)
Is it possible to do this directly without User table(s) in database? Do I really need to copy over entries from Firebase Auth to Firebase Database? Shouldn't there be a better way to get non current users from Firebase? Does anyone else think that's really bad design (for just being able to read an entry)? Aren't I then responsible for the maintenance of those entries?
Appreciate pointers to specific reading too!
For clarity, I'm not trying to query an entry in the realtime database tab of Firebase Console. I'm trying to query the User object that Firebase saves from Facebook (which is visible in Auth' tab of Firebase Console).
To access the other users make your JSON structure like this:-
Users:{
userID1 : {...//PERSONAL DATA},
userID2 : {...//PERSONAL DATA},
userID3 : {...//PERSONAL DATA}
},
users_Data:{
userID1 : {...//data to be shared like userID, name..etc},
userID2 : {...//data to be shared like userID, name..etc},
userID3 : {...//data to be shared like userID, name..etc}
}
Just access those users_Data to store in your dataSource in which you will have your usersID's to access.

iOS Swift Firebase - How to make Current user data available throughout the app?

I'm building an app using Firebase Database, Auth & Storage.
Basically, my users can sign up / sign in via Firebase Auth, then I store data for each of these users on Firebase Database.
I have been looking for the best way to get the Current User's data available throughout the app without having to re-fetch the information I need from Firebase on different screens.
Very few resources on Stack. Some people do talk about creating a CurrentUser singleton available globally, but others say that it's bad practice.
Now, maybe fetching the data as and when I need them might be the best practice solution, but I just felt it was strange to re-write the same piece of code again and again on each screens.
Any thoughts on this? I'd be really grateful if you guys have insights.
Isn't the logged in user data already available globally? I mean you can always access any logged in user related data by -
let userID = FIRAuth.auth()!.currentUser!.uid
The way uid available in this code snipped you can access any data related to user as long as the user is logged in. Do check if the current user is not nil before executing that, but i assume you will anyways take care of that.
You can make a struct with which you can access the values:-
struct userGlobalInfo{
var userID : String!
var userName : String!
var userEmail : String!
init(id : String!, name : String!, email : String!){
self.userID = id
self.userName = name
self.userEmail = email
}
}

Resources