Unable to write value to firebase Swift 3 - ios

I have enabled email (custom) authentication in Firebase , I have set following rules
{
"rules": {
".read": "auth == null", //even non-authorised users CAN read
".write": "auth == null" //even non-authorised users CAN write
}
}
Trying to test Firebase using following code
let rootRef = FIRDatabase.database().reference()
let playlists = rootRef.child("playlists")
playlists.setValue("test")
Not sure whats wrong but it gives setValue and RemoveValue Permission denied error.
Unable to understand whats wrong , I am new to Firebase

Here's the solution
{
"rules": {
".read": true,
".write": true
}
}
These rules allow everyone to read and write to your entire Firebase node. Ok for testing but please don't leave it like that as it's totally insecure.
Also note that your code will erase the playlists node each time and overwrite it with
your_firebase_ref
playlists: "test"
Just to get you going in the right direction, here's updated code that will create a new node each time it's run.
let rootRef = FIRDatabase.database().reference()
let playlists = rootRef.child("playlists")
let aNewPlayList = playLists.childByAutoId()
aNewPlayList.setValue("test")
which will result in
your_firebase_ref
playlists
-Yius889jsijs: "test" //first time it's run
-YUOmsooaosd9: "test" //second time it's run
the -Y.... key is created by childByAutoId and is generally the way to create children keys within a node.

Related

Swift Firebase -How to block a phone number from posting to a certain ref

My app is a food app, I let users post food. Before someone posts there is warning that says "food and drinks only, anything else and you will be banned from posting for life"
let dict = ["pictureOfCouchUrl...": "foodPic"]
let postsRef = Database.database().reference().child("posts").child(uid)
postsRef.updateChildValues(dict)
In the above example this user posted a pic of a couch. I see it and now I want to ban this user from posting anything at this ref.
When a user signs up to my app they use their email address to create an account but before they post they have to verify their phone number which eventually links their email address and their phone to their uid. The phone number is only used for posting and to login they use their email address.
let credential = PhoneAuthProvider.provider().credential(withVerificationID: verificationId, verificationCode: verificationCode)
Auth.auth().currentUser?.link(with: credential, completion: { [weak self](authDataResult, error) in ...
I also let users completely delete their account which removes their email, phone number, and uid:
let user = Auth.auth().currentUser
user?.delete { (error) in
if let error = error { }
}
The problem is if I want to block the uid of the user who posted the couch, all they have to do is completely delete their account, create a new one, and then get a brand new uid, which means this process can repeat itself. But if I use their phone number (which they will eventually have to register to post) to block them from the postsRef then even if they create a new account and get a new uid they still won't be able to post using that phone number (I don't see anyone changing phone numbers just to be able to post on my app).
How can I block a user from posting using their phone number? instead of their uid?
Here are my rules:
"posts": {
".read": "auth.uid != null",
"$uid": {
".write": "$uid === auth.uid"
}
}
I created a ref specifically for blocked phone numbers:
"blockedNumbers": {
".read": "auth.uid != null",
"$uid": {
".write": "$uid === auth.uid"
}
}
Whenever a user's phone number is blocked the number is permanently in that node. Even if a user deletes their account that phone number will always be inside there. Before a user posts I simply check to see if that phone number is in there and if it is they can't post.
let dict = // dict with data that needs to get uploaded
let userPhoneNumber = "+12125551212"
let blockedNumbersRef = Database.database().reference().child("blockedNumbers").child(userPhoneNumber)
postsRef.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
// show alert because this phone number is blocked
return
}
// the phone number doesn't exist so go ahead and post
let postsRef = Database.database().reference().child("posts").child(uid)
postsRef.updateChildValues(dict)
})

Firebase database not working

I am working on an app that requires Firebase auth and database. When registering, the app must check to see if the code entered is genuine then proceed to create a user. This is my code:
ref = FIRDatabase.database().reference()
self.ref.child("PatientCodes").observeSingleEvent(of: .value, with: { (snaphshot) in
print("In")
let value = snaphshot.value as! NSArray
if value.contains(self.patientIDTextField.text!) {
print("Found ID")
FIRAuth.auth()?.createUser(withEmail: self.emailTextField.text!, password: self.passwordTextField.text!, completion: { (user, error) in
if user != nil {
self.performSegue(withIdentifier: "registered", sender: self)
}
})
}
The issue I am having is, none of the code is being executed past self.ref.child etc. The print("In") statement is never hit.
Any help is greatly appreciated. Thanks in advance.
Before starting anything with Firebase Database,
first check that the rules under database in your console is properly set i.e whether read is true or write is true.
Look at your database's rules, the default rule prevents anonymous read and write, so you must log in first to get access to the database.
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}

How do I validate if a username exists before sign up in Firebase and Swift 3?

I've incorporated a sign-up flow of five view controllers as opposed to one central VC for Firebase sign up.
Normally there would be the problem of data being lost before sign up, but I'm pushing all the entered values via segue programmatically to the final confirmation page and sending the data to Firebase for auth eventually.
My problem lies therein - I want to check if the username exists before signing up. This is imperative for the UX of my app and I can't just do this all one VC.
Here's the code I have for the UsernameViewController:
let rootRef = FIRDatabase.database().reference()
rootRef.queryOrdered(byChild: "users").queryEqual(toValue: self.chooseUsernameTextField.text!)
.observe(FIRDataEventType.value, with: { (snapshot: FIRDataSnapshot!) in
if snapshot.hasChildren() == false {
print("not found")
} else {
print("usr found")
}
});
Here's the code I have for the Firebase Database Security Rules
{
"rules": {
".read": false,
".write": false,
"users": {
".validate": "data.child('username').val() === true",
".read": true
}
}
}
and finally, a screenshot of my Data tree (My Data tree won't let me nest any new users or create a user node without a value for some reason):
Picture of Firebase Data Tree: App Name/Users
I have a nagging suspicion that my data rules and tree are configured properly to match the code, but I'm stuck in a XY problem of not knowing what to do for Firebase security to get my code of username checking complete.
Please help me! :(
If there's a user created within the Auth section of Firebase as well, then you can actually use the fetchProviders method, and if no providers are returned, you have no user in you Auth section.
FIRAuth.auth()?.fetchProviders(forEmail: email, completion: { (providers, error) in
if providers == nil {
// user doesn't exist
} else {
// user does exist
}
})
I have not tested the code but the theory would be to order by username that equals to the username you are looking for. If this snapshot exists you already have a user in the database with that name if it doesn't you do not.
Will run and test code when i have a chance to and update my post.
let rootRef = FIRDatabase.database().reference()
rootRef.child("users").queryOrdered(byChild:"username").queryEqual(toValue: self.chooseUsernameTextField.text!)
.observe(.value, with: { (snapshot) in
if snapshot.exists() == false {
print("not found")
} else {
print("usr found")
}
});
When using Firestore to save users, I did this
Auth.auth().addStateDidChangeListener({ (auth, user) in
if let id = user?.uid{
let userDoc = db.collection("users").document(id)
userDoc.getDocument { (document, error) in
if document == nil{// if user doesn't exist, create one
self.db.collection("users").document(id).setData(userProfile.toDictionary()){ error in
}
}
}
}
})

Firebase,Swift : Not able to retrieve Data

I'm trying to do a simple task, to get a value from Firebase.
I added the image below with the relevant screens. At least one of the print commands should print a message to the console, but it's not working. I've been trying to fix it for 1,5 days now and I still have no idea why it is not working.
Code:
import UIKit
import Firebase
class MainVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let ref = FIRDatabase.database().reference()
ref.child("zonneschijn").observeSingleEventOfType(.Value, withBlock: { snapshot in
if snapshot.value is NSNull {
print("Does not exist currently")
} else {
print("Exists currently")
}
})
}
}
I've also tried to use viewDidAppear, also with no succes.
If you are not authenticating your user's.
Go to Rules tab in Realtime Database in Firebase console.
Default Security rules of firebase are something like this :-
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
Which means only authenticated users(Those user's who have been signed in either with gmail, Facebook, gitter, email..etc) can read or write data...
Clearly you are not authenticating your user that's why you can not retrieve any data with these default Security Rules .
This is a good practice to only allow authenticated users to have access to the DB, as it is more secure.If you want to read data anyways , Modify your security rules to this:-
Warning :- Not Recommended if you are making app that has some private DB.
// These rules give anyone, even people who are not users of your app,
// read and write access to your database
{
"rules": {
".read": true,
".write": true
}
}
A better alternative :-
Sign in your user(start with email-password : Firebase EMAIL- Password Auth)
Then access your DB
Do read : Security Rule's

Firebase Object Ownership with Event Observation

I'm using Firebase in my iOS app. I'd like each of my objects to have a creatorId property whose value is the authenticated user ID (authData.uid with a Firebase authentication object). I'm using a custom token generator for authentication, but the problem can be reproduced with anonymous log in too.
I'd like a user to only be able to read (and write, but let's focus on reading right now, as that's where I'm having my issues) objects that they created. In other words, the querying user's authenticated user ID will match the creatorId of the objects they are fetching.
I'm having trouble with permissions when I craft queries and rules to make this happen.
Here is the Firebase documentation for Rules and Security.
Here is what my Firebase dashboard looks like for a Task object:
+ firebase-base
+ tasks
+ {task_id}
+ creatorId:
+ title:
where task_id is a unique identifier generated by Firebase upon insertion.
My rules look like this (again, let's ignore writing rules for now):
{
"rules": {
"tasks": {
"$task_id": {
".read": "auth.uid === data.child('creatorId').val()"
}
}
}
}
Reading a specific task works fine, but I'd expect to be able to make a query that says, "fetch all the tasks that I created" using observeEventType and related functions. This doesn't work for me. I get "Permission Denied" errors.
Here is how I'm observing, in Swift:
let reference = Firebase(url: "https://{My-Firebase-Base-Reference}/tasks")
reference.observeEventType(.ChildChanged,
withBlock: { (snapshot: FDataSnapshot!) -> Void in
// Success
}) { (error: NSError!) in
// Error: I get Permissions Denied here.
}
Per #Ymmanuel's suggestions, I also tried being more specific in my query, like so:
let reference = Firebase(url: "https://{My-Firebase-Base-Reference}/tasks")
reference.queryOrderedByChild("creatorId").queryEqualTo({USER_UID}).observeEventType(.ChildChanged,
withBlock: { (snapshot: FDataSnapshot!) -> Void in
// Success
}) { (error: NSError!) in
// Error: I get Permissions Denied here.
}
Neither of these blocks work, I always get "Permission Denied" errors. What am I doing wrong?
What you are missing is that you are assuming that security rules are queries and that is not true.
Check the
Rules are Not Filters section in the link.
Security rules only validate if you can read or write a specific path of your firebase database.
If you want to only receive changes of a specific user you should use firebase queries.
For example if you want to get all the tasks of a specific user, you should do:
let reference = Firebase(url: "https://{My-Firebase-Base-Reference}/tasks")
reference.queryOrderedByChild("creatorId").queryEqualTo(YOUR_CURRENT_USER_UID).observeEventType(.ChildChanged,
withBlock: { (snapshot: FDataSnapshot!) -> Void in
// Success
}) { (error: NSError!) in
// Error: Get Permissions Denied Here.
}
This way you could get all the events only related to your user, and protect the information by applying the security rules.
also if you want to allow only the creator to write their own tasks you should also consider the case where you create the task and write something like this:
"tasks": {
//You need to include the $task_id otherwise the rule will seek the creatorId child inside task and not inside your auto-generated task
"$task_id": {
".read": "auth.uid === data.child('creatorId').val()",
//this is to validate that if you are creating a task it must have your own uid in the creatorId field and that you can only edit tasks that are yours...
".write":"(newData.child('creatorId').val() === auth.uid && !data.exists()) || (data.child('creatorId').val() === auth.uid && data.exists())",
//you should add the index on to tell firebase this is a query index so your queries can be efficient even with larger amounts of children
".indexOn":"creatorId",
}
}
(check the syntax but that's the general idea)
A couple of comments:
If local persistence is on, and you have no internet connection and there is no local value, neither of the blocks will be called.
The observe code in the question is bugging me, it's not wrong but may be a bit clearer if it was like this:
reference.observeEventType(.ChildChanged, withBlock: { snapshot in
print(snapshot.value)
}, withCancelBlock: { error in
print(error.description)
})
Edit:
The question is clearer now
I'd like a user to only be able to read objects that they created.
I'll demonstrate via the following:
A Firebase structure:
posts
post_0
created_by: uid_0
msg: some message
post_1
created_by: uid_1
msg: another message
post_2
created_by: uid_2
msg: yippee
and the rules that will allow a user to only read from the post node they created
"rules": {
".read": false,
".write": false,
"posts": {
".read": false,
"$post_id": {
".read": "root.child('posts').child($post_id).child('created_by').val() == auth.uid",
".write": "auth != null"
}
}
}
then the code to test. We assume the current users uid is uid_0:
let reference = self.myRootRef.childByAppendingPath("posts/post_0")
reference.observeEventType(.ChildAdded, withBlock: { snapshot in
print(snapshot.value)
}, withCancelBlock: { error in
print(error.description)
})
The above code will allow a read from node post_0 which is the post uid_0 created as indicated by the created_by child.
If the path is changed to posts/posts_1 (or anything else) for example, the read is denied.
This answer my not directly answer the question as if you are expecting the rules to 'block' or 'filter' the resultant data, that isn't what rules are for (per the other answer).
So you may want to go down a different path by constructing a query to pull out just the posts you want to work with based on the created_by = auth.uid like this
let reference = self.myRootRef.childByAppendingPath("posts")
reference.queryOrderedByChild("created_by").queryEqualToValue("uid_1")
.observeEventType(.Value, withBlock: { snapshot in
print(snapshot.value)
}, withCancelBlock: { error in
print(error.description)
})

Resources