Link between user and the database in firebase - ios

Swift 3.0 iOS 11.x
So link my swift app to a firebase using the standard
FirebaseApp.configure()
I log into it with a user I have created, authentication registers good and I create a database on it. All good.
I take a different device and try log in with a user who doesn't exist, authentication fails and I am unable to access the database. All good.
But wait, I create another user; and given I know the path of the database I find I have access to it. Which would seem reasonable, and yet it isn't.
Imagine I have 10 different app users, they all have their own databases; and yet as long as they are authenticating to firebase, they potentially mess each other up since everyone seem to have access to everyone else's database as long as they have authenticated. Indeed I look at this post...
How to link between Authenticated users and Database in Firebase? which seems to suggest things are a little tighter, and yet evidently their not.
I'v missed something fundamental here haven't I?

This is secured through Firebase Rules. You can check out the documentation for full detailed information about the topic.
The structure you are looking for would look something like this:
{
rules: {
$uid: {
".read" = "auth.uid == $uid",
".write" = "auth.uid == $uid"
}
}
}
You have to be careful with this one because this does not apply to every database structure. This one would work, if you create a node for every user in the root of your database and specify the users authentication id as the key. A user could only access the data in the node with his Firebase Authentication id, although in that node all data, also every child node of it.
Check out the docs for more information. You can find your rules in the Firebase Console in the Database tab.

Related

What do these default security rules for the Firebase Realtime Database mean?

I've created a new project on Firebase, and created a Realtime Database in there. When asked about the security rules for my database, I selected to Start in test mode.
Now the security rules of my database in the Firebase console show up as:
{
"rules": {
".read": "now < 1622790000000", // 2021-6-4
".write": "now < 1622790000000", // 2021-6-4
}
}
What do these rules mean? And how can I change them to be more secure?
It's been a month since I created my Firebase Realtime Database, and I now got a message:
Your project's Realtime Database '' will start denying client requests unless you update your security rules
These default test mode rules are a simple catch-all that allows everyone in the world to read from and write to your database until a given date.
Let's break the rules down to see exactly how they work:
The ".read" and ".write" nodes immediately under "rules" determine who can read/write the data in the entire database.
The now variable is automatically set by Firebase to be the current time on the server. This value is in milliseconds since the epoch, which is the recommended value to also store timestamps in Firebase.
The 1622790000000 value in the rules is the timestamp of some point in the future. Let's see what this value is in a more readable date format:
console.log(new Date(1622790000000))
"2021-06-04T07:00:00.000Z"
So anyone can read of write all data in our database until June 4th, 2021. After that date nobody can access the data anymore with the client-side SDKs. The Firebase Admin SDKs bypass these rules altogether, so they are not affected.
Can I extend the time period?
You may have gotten a message like this from Firebase:
You chose to start developing in Test Mode, which leaves your Realtime Database instance completely open to the Internet. Because this choice makes your app vulnerable to attackers, your database security rules were configured to stop allowing requests after the first 30 days. In 5 day(s), all client requests to your Realtime Database instance will be denied.
This message means that access to your data is about to expire, due to timestamp that is in your security rules.
It's actually pretty easy to extend the test mode to another deadline. All you need to do is change that 1622790000000 value. For example, for extend it to July 4th, I can set the value to 1625382000000.
To determine the value to use, I run this tiny JavaScript snippet:
console.log(new Date("2021-07-04T07:00:00.000Z").getTime())
Run this snippet to get the timestamp exactly one month from now:
console.log(new Date(Date.now()+30*24*60*60*1000).getTime())
Here's another tool to calculate these values.
By using 1625382000000 we've extended test mode for a month and everyone can read/write the entire database until July 4, 2021.
How can I better protect the data?
At some point you should come up with a better way to protect your (user's) data than just opening it until a specific date. I typically do this right when I start a project, but it's also fine if you start it a bit later.
The important thing is that you should treat the server-side security rules the same as the client-side source code of your app.
I develop my code and rules in tandem. So:
I start with a fully closed off database, since there is no code yet that needs access to any data.
I add some data manually to the database, and write code to read it. At this point, I write security rules that only allow read-access to that specific data. So it may be ".read": true, but it'll be much deeper in my JSON structure. Even such simple rules will already block many bad actors.
The first time I want the app to write to the database is also when I add authentication. Typically I start with anonymous auth, since it does not require me to enter any credentials.
I then include the hard-coded UID in my security rules, to ensure only I can write data. You'll often still find this top-level ".write": "auth.uid === 'hardcodedUidOfPufsAnonymousUser'" in my rules much later, after I added proper data ownership.
When using Firestore I sometimes evolve that as explained here: User conflict when using same Auth method for Admin and Normal users | Firebase Auth
At any point when I add (typically lists of) data, I think through who "owns" this data, and who can read it. I then expand my rules to allow exactly that access, and nothing more.
This need to update my security rules as I write code slows down the pace at which I code, but I'll gladly do it anyway. Keeping the data in my database secure at every step, allows me to give people access to the app/database with confidence. I recommend you do the same.
For more information, I recommend reading:
The Firebase documentation on security rules, which contains examples of these common use-cases:
Content-owner only access
Public read, private write access
Attribute and role based access
All authenticated users can read/write all datsa

Azure - App Insights - how to track the logged-in Username in Auth Id?

What is the best-supported approach for tracking logged-in Usernames/Ids in App Insights telemetry?
A User with Username "JonTester1" said some Pages he visited 4 hours ago were really slow. How can I see everything JonTester1 did in App Insights to trouble shoot/know which pages he's referring to?
Seems like User Id in App Insights is some Azure-generated anonymized thing like u7gbh that Azure ties to its own idea of the same user (thru cookie?). It doesn't know about our app's usernames at all.
I've also seen a separate field in App Insights called Auth Id (or user_AuthenticatedId in some spots), which looks to sometimes have the actual username e.g. "JonTester1" filled in - but not always... And while I don't see any mention of this field in the docs, it seems promising. How is our app's code/config supposed to be setting that Auth Id to make sure every App Insights log/telemetry has it set?
Relevant MS docs:
https://learn.microsoft.com/en-us/azure/azure-monitor/app/usage-send-user-context
This looks to just copy one library Telemetry object's User Id into another... no mention of our custom, helpful Username/Id anyway... and most in-the-wild examples I see don't actually look like this, including MS docs own examples in the 3rd link below; they instead hardcode get a new TelemetryClient()
https://learn.microsoft.com/en-us/azure/azure-monitor/app/website-monitoring No mention of consistently tracking a custom Username/Id
https://learn.microsoft.com/en-us/azure/azure-monitor/app/api-custom-events-metrics#authenticated-users Shows some different helpful pieces, but still no full example. E.g. it says with only the setAuth... JS function call (still no full example of working client-side JS that tracks User) on the page, you don't need any server-side code for it to track custom User Id across both client-side and server-side telemetry sent to Azure... yet then it also shows explicit code to new up a TelemetryClient() server-side to track User Id (in the Global.asax.cs or where?)... so you do need both?
Similar SO questions, but don't connect the dots/show a full solution:
Azure Insights telemetry not showing Auth ID on all transactions
Application Insights - Tracking user and session across schemas
How is Application insight tracking the User_Id?
Display user ID in the metrics of application Insight
I'm hoping this question and answers can get this more ironed out; hopefully do a better job of documentation than the relevant MS docs...
The first link in your question lists the answer. What it does show you is how to write a custom telemetry initializer. Such an initializer lets you add or overwrite properties that will be send along any telemetry that is being send to App Insights.
Once you add it to the configuration, either in code or the config file (see the docs mentioned earlier in the answer) it will do its work without you needing to create special instances of TelemetryClient. That is why this text of you does not make sense to me:
[…] and most in-the-wild examples I see don't actually look like this, including MS docs own examples in the 3rd link below; they instead hardcode get a new TelemetryClient()
You can either overwrite the value of UserId or overwrite AuthenticatedUserId in your initializer. You can modify the code given in the docs like this:
if (requestTelemetry != null && !string.IsNullOrEmpty(requestTelemetry.Context.User.Id) &&
(string.IsNullOrEmpty(telemetry.Context.User.Id) || string.IsNullOrEmpty(telemetry.Context.Session.Id)))
{
// Set the user id on the Application Insights telemetry item.
telemetry.Context.User.AuthenticatedUserId = HttpContext.Current.User.Identity.Name;
}
You can then see the Auth Id and User Id by going to your AI resource -> Search and click an item. Make sure to press "Show All" first, otherwise the field is not displayed.
Auth Id in the screenshot below is set to the user id from the database in our example:
We access the server from azure functions as well so we set the user id server side as well since there is no client involved in such scenarios.
There is no harm in settting it in both places, javascript and server side via an initializer. That way you cover all scenario's.
You can also manually add user id to app insights by
appInsights.setAuthenticatedUserContext(userId);
See App Insights Authenticated users

Configuring Permissions and Sharing Realms across Multiple Users

I'm using Realm Swift and the Realm Object Server as the storage solution for an app I am working on. I could use a traditional server with a relational database, but I really don't need the server to do any real work. The only backend that I really need is just storage and data syncing. Realm seems to provide exactly what I want.
So far, I have a working example of a realm. The problem that I have started running into is access control. I feel like I may have a fundamental misunderstanding of what Realm can provide me, and there are not a ton of fantastic resources out there. The Realm documentation is pretty detailed, but it doesn't have the best working examples.
My app will be used to track teams from an available set of players. The set of players will be relatively constant and unchanging. The teams, however, will change frequently. With this in mind, I had the following idea of my Realm setup:
A single Realm containing the set of all players: /Players. Every user should have read access to this realm but only admins should have write and manage.
A single Realm for each user of the application /~/MyRoster. This realm should be read/write by that user. I think that the user should be able to grant another user temporary read/write access to their realm as well.
Multiple users should be able to form a team wherein they can read (and potentially write) all team users' rosters.
Does this sound like an acceptable use of the Realm backend? How should I manage the central shared data pool? Should I just create a /~/MyRoster realm for a user as soon as they register? How could I configure the permissions the way that I want them? To me, the permission structure seems very strange. It seems like I can use the PermissionOffer/PremissionOfferResponse constructs to achieve the Realm sharing that I want.
Any help would be greatly appreciated.
Thanks for the detailed write-up. Your proposed architecture seems like a good fit. Here is what I would recommend:
For the global /Players Realm, I would create that within development. Today this is a bit clunky in that you have to use a client SDK to open the Realm as an admin user (because only admin users can create Realms outside of their scope /~/ directory). You could create a code path in your app that if you sign in as an admin, this opens the /Players Realm and then apply a permission change to that Realm:
let permission = SyncPermissionValue(realmPath: "/Players",
userID: "*", // To apply to all users
accessLevel: .read)
user.applyPermission(permission) { error in
if let error = error {
// handle error
return
}
// permission was successfully applied
}
This code path doesn't need to be run more than once. Our plan is to add this kind of functionality into the browser in the dashboard so you can just manually create a global Realm and adjust permissions without using a client SDK.
For the user-specific Realms, you do not need to create them immediately, because the Realms will get lazily created when the user eventually needs it. What I mean is that Realm is setup such that you can open a Realm on the client synchronously (enabling the offline-first capability), and then after the first creation when the client syncs to the server, will the server actually become aware and also create the Realm within it. Thus you client code just needs to be setup to open the Realm at /~/MyRoster when necessary.
As for granting permissions to other users, this would use the Swift access control APIs to apply a permission change to the user's Realm and grant access to the other user. This can happen dynamically, such that the user can grant, then revoke the permission as needed.
For the third part with regards to groups, I would create another global Realm, /Group, that has read/write access to all users which holds the representation of the groups. For example you could have a Group object which contains a list property linking to User objects, with 1 User per user within your application. This way every user could make changes to the Realm to represent the groups he/she is part of.
You could setup a Realm Function to listen to changes to the /Group Realm such that when a user is added to a group or removed, the function would trigger the necessary permission change to the various Realms in the group.
Now if your are concerned about access control to the /Group Realm, you could instead, setup a Realm Function that uses an admin user to apply changes to /Groups, listening to a user-specific Realm /~/MyRequests where a user could write an object to this Realm which would trigger the function to make a change to the group Realm. In this manner you could prevent unauthorized requests, and keep the /Group Realm as read-only for all users but admins.

Firebase not observing until accessed once [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
Firebase is configured in APPDelegate.
Read is set to .true in rules.
But, before I have a user sign into my application, I am checking for usernames, within a separate table of my Firebase DB. It would skip the observation block entirely, until I forced signed-in into another already created account of mine. And then when I would re-build the application the block is no longer skipped and actually accesses the table.
How do I "Wake Up" Firebase when I run the app on a fresh device, so I can check for usernames without having to log onto the app with an existing account before hand?
You may want to reconsider how the app is structured.
If you are checking for usernames before being authenticated that means the node is exposed and anyone can grab a copy of all the usernames. Obviously that in itself may not be a huge issue but if your users decide to use an email, whoever grabs the list has a instant list they can spam to.
When you add any observer to a node, that node is read once as soon as the observer is added. When you app starts you can use .childAdded to iterate over an existing node to pre-load some data, a grocery list for example, and then any new grociees added after that will be sent your your app via an event.
Likewise a .value event will read in an entire node and leave an observer attached for any future events.
The username issue is tricky and the way you are doing is now is probably going to get you in trouble in the long run.
A better way is to leverage Firebase Authentication.
Firebase handles all of the usernames and passwords for you. It's very powerful and flexible and avoids the issues you are encountering. It will let you know if user names exist or not, it will do password reset emails and you can manage users from the Firebase Console. It's the way to go.
If you want to add username functionality it can be pretty easily done by adding a username or nickname node to the /users node
/users
uid_0
email: "bill#email.com"
username: "bill_the_cat"
uid_1
email: "clark#email.com"
username: "superman"
Once the user authenticates using Firebase Authentication, from there forward any time the user info needs to be displayed in the app, simply look up the uid that you need (uid_1) and grab the username node (superman) for display.

How to structure properly Firebase Database?

I read some Firebase database structure guides on how to structure your data properly (without data nesting) but I have one question.
So, I have an iOS app that uses Firebase database. The users need to login/register.
In terms of data structure, my database looks like this:
-Database
---Users
-----User1
--------username: johndoe
---------email: johndoe#test.com
---------display_name: John Doe
-----User2 {....}
-----User3 {....}
Now, let's imagine I have 100K users in there. Every time a new user is being registered, I check if the username & the email already exist in the database, if they don't then create the new user account.
My question is - Do I need to create a new object that contains only the usernames and another that contains only the emails? I'm asking this because I'm concerned that if I iterate through the Users objects I will potentially be downloading hundreds of megabytes just to check if the username and the email already exist.
Firebase will not allow duplicate users (authentication names). So when you call createUser, firebase will return an error if the user already exists.
Secondly, if you are performing a query for a specific item in Firebase, you are not downloading anything unless that item is found. So whether its 10 or 100k user nodes, nothing is downloaded when performing a query other than the nodes that match the query, which would only be one if there was a duplicate user. again though, this is not needed since Firebase rejects duplicate authentication names.
And to clarify; there is nothing wrong with nesting nodes. However, keeping them flat is usually better depending on your use case. So don't go overcomplicating your structure if you don't need to.
Oh, and your Firebase structure is spot on. Keep going with that.

Resources