Firebase authorisation and registration - firebase

I am trying to create a basic app for my small educational business.
We supply English teachers to schools and I want a way for parents to access the progress reports and other data about their children.
I'm using Android Studio and Flutter as well as Firebase to store the data. I don't want parents to be able to access the data of every child, obviously, but they may have more than one child at the school.
So I need to limit their read privileges to just the records that relate to their children. I'd like to do that by giving them some sort of registration code that they could use the first time they access the app so that we can ensure that they are only being given access to the correct records. Subsequent logins would then be via email and password.
Is there a way to do this with Firebase?

Your use-case sounds feasible, but it is really broad which makes it impossible to answer it completely-yet-succinctly.
Specific on what to do with the registration code that associates a parent with their children, when you generate the registration code, you write that code and the associated children to a database. Then when the parent registers with that code, you associate their account (UID) with the code. You'll just have to ensure that the code is sufficiently long and random that it can't be reasonably guessed by another user.

Related

How to structure Firestore Security Rules & Data Structure for granular access

I am building a community-type app based on Firestore where users should have granual control over what kind of information they share with whom.
Users can have properties such as name, birthdate, etc. and for each of them they can decide to share it with the one of the following groups/roles:
Private
Contacts
Admin (Admins of organizations that user is a member of)
Organization (Members of organizations that a users is a member of)
Public (All users of the app)
As documents in Firestore will always be retrieved as a whole, I already know that I somehow will have to segregate my user properties by access level.
I've got two approaches so far:
Approach 1
Store each user property in a separate document that contains a field access level
Store some metadata in, for example /user/12345/meta/roles, so that I can point the security rules to those documents to validate access
Benefits:
Easy structure
Flexibly
(Almost) no data duplication
Drawbacks:
Lots of document reads for getting a user's profile
Approach 2
Store user profile in, for example /user/12345/profile/private and duplicate the public information into /user/12345/profile/public, and do the same for each access level
Benefits:
Reduced document reads
Drawbacks:
Complexity
It feels wrong to duplicate that much data
Does anyone have any experience with this and any suggestions or alternative approaches they can share?
Follow-up question:
Let’s say I store the list of members of an organization in a subcollection, that is only accessible for members of the organization (for privacy reasons). Doesn’t that mean that when querying that list of members from client side, I have to do it „blindly“, meaning I can’t know if the user can access that document until I actually try? The fact that the query might fail would tell me that the user is not actually a member of that organization.
Would you consider this kind of query that is set up for failure bad practice? Are there any alternatives that still allow to keep the memberlist private?
I think you are moving from a SQL environment to NoSql now which is why you are finding the Approach 2 as not the right way to proceed.
Actually approach 2 is the right way to proceed there are couple of advantages
1.) Reduced Document Reads - More cost savings. Firestore charges by number of reads and writes if you are reducing no of reads and writes optimally its always the way to go for. Also the cost of storage due is increased reads will always be less than the actual cost of reads if you are scaling up your application.
2.) In NoSql database your are allowed to duplicate data provided it is going to increase the read / search speed from the database.
I am not seeing the second approach as complex because that's the tradeoff you are making when Choosing a NoSql over Sql

GraphDB account modeling: user access relationship attribute or relationship?

I am attempting to model account access in a graph DB.
The account can have multiple users and multiple features. A user can have access to many accounts. Each account can give access to only part of the features for each user.
One way I see it is to represent access for each user through relationship attributes, this allows having a shared feature node.
user_1 has access to account_1-feature_1 and account_2-feature-2. user_1 does not have access to account_1-feature_2 even though it is enabled for the account.
Another way to model the same access, but without relationship attribute is to create account specific feature nodes.
Question 1: which of these 2 ways is a more 'proper' modeling in the graph DB world?
Now to make things more interesting the account can also have parts which can be accessed by multiple accounts and a certain feature should be able to be scoped down to only be accessible for specific part by user.
In this example user_1 can access account_1 only for part_a feature_1.
To me it seems like defining an attribute on relationship is the way to go for being able to scope down user access by feature & by part of the account. However, reading neo4j powerpoints this would be one of the code smells of relationships having "Lots of attribute-like properties". Is there a better way to approach such problem in a graph?
I could be wrong here, but here are my thoughts. Option 1 definitely sounds the better way from a modeling perspective, however, I don't see how you can keep the data consistent without building heavy machinery to do it. For example, If someone deletes Account1.Feature1, and does not update the edge from User1 -> Account1, then you end up having stale RBAC rules in the system. You think you have access to something, but in reality that "thing" does not exist anymore. Option 2 may not seem very attractive from a data model perspective, but it does keep your data consistent. If you delete Account1.Feature1, the edge is automatically deleted in the same transaction.
The only con is that, you need to incur additional cost at insertion where you need to insert a lot more nodes than Option 1. For an RBAC system, I think its a fair compromise.
The same comment applies to the second half of your question as well.

Reading list with Dialoglfow fulfillment from Real-Time Database

I am creating a chatbot using Dialogflow and Dialogflow's Inline Editor (for Cloud functions and Firebase database "Real-Time Database"). I will integrate this chatbot with Google Assistant.
I have to read a list from the database, wherein the list has several children, few of them have sub-children, and few of sub-children have sub-sub-children. Because the output is a list and consists long-text, it will take too long to speak all data at once. So I would like to output one child from the list and ask the user for permission (Yes/No) as "Do you want to read the next?". If the user says "Yes", I will continue reading likewise until the end. And if the user says "No", I will trigger an event. Asking for the permission from the user is true before reading a child, even sub-child, and even sub-sub-child.
The approach that I have taken involves creating a separate DB record for each user when they first request the list, to keep track of where they are in the list. When the user says yes, get user’s current item id from the database, get the next item in the list (return it to the user via agent.add) and then update the user’s DB record to the next item’s id and so on until the user reaches the end of the list. After agent.add(), ask for the permission from the user by agent.setFollowupEvent(). If the user says no, just reset/delete the DB record for that user.
Few questions I would like to ask:
How will I identify each user as an individual: by some id, session, or something else?
When I run the below code in return cloud function, agent.add is overridden by agent.setFollowupEvent. How do I stop this?
agent.add('I will print the list here!');
agent.setFollowupEvent('SOME_EVENT'); //invoking an intent to ask for the permission.
You have a few issues you're trying to raise here, in addition to the one you're dealing with. Looking at each:
How can I stop setFollowupEvent() from overriding the message I've set?
You don't. The entire point of setFollowupEvent() is to switch to a different Intent instead of the one that is currently being processed.
Most of the time you think you want setFollowupEvent(), you probably don't. Don't use it.
So how can I add the question at the end of what I'm saying?
Just ask it.
Really, it is that easy.
You can either include it in the string you're sending to agent.add(), or (depending on the details), you can do a second add() with the prompt.
But don't I need to trigger an Intent to get the answer?
No. That isn't what an Intent is for.
Intents capture what the user is saying, not what you are asking or what your agent is doing. Your fulfillment does something based on both the Intent that is triggered as well as the rest of the state that you know about the conversation. But the Intent is just one bit of that information.
You mentioned user state. How can I keep track of the user state during the conversation?
Since it looks like you're using the dialogflow-fulfillment library, the easiest way is to store your state in the parameters in a Context with a very long lifespan (or that you keep renewing).
So the first time your fulfillment is triggered, you can check the Context. If the Context or ID aren't there, then you would generate a random user ID and store it in the Context. Subsequently, you would use this ID to look up the user's information in the real time database.
If I'm doing this work, do I need the database?
Nope! If you are just storing a little bit of information about the user, and the information will just last the lifespan of the conversation, you can store all of it in Context parameters directly. You do need to make sure that these parameter names don't conflict with any parameters that your Intents have, but otherwise these will last las long as the Context does.
If you need to store information about this person in between conversations, then you will need to look into other methods. There is a User ID available for Actions, but this is deprecated and scheduled to be removed. There are also session storage and user storage fields that the Assistant makes available, but these are a little tricker to use using the dialogflow-fulfillment library if you don't need them.

Allow users to see each other's data in firebase

Is there a why to let two user login with their own email and password and see the same data?
(Maybe one user well login and be transferred to the other user that has all the data).
Thanks,
Zvi Karp
What you were trying to do is about Database Design, it's actually not about Firebase. (watch out: the link is about relational database design, but Firebase is not using relational database. The idea is the same though.)
There are many ways to achieve your goal. Since you didn't describe your question clearly, I'll just give a general solution:
add a key sahredData to your User entity, and the value of this key is the id of the data you want to share between users. Different users can use the same value in this field(which means they share the same data).
whenever a user needs to access the shared data, use the value of sharedData, which is the id of the shared data, to access the data.

Is there an inherent risk in publishing other users' ids?

I have a collection called Vouchers. A user can, if they know the unique number ID of a Voucher, "claim" that voucher, which will give it a user_id attribute, tying it to them.
I'm at a point where I need to check a user's ID query against the existing database, but I'm wondering if I can do so on the client instead of the server (the client would be much more convenient because I'm using utility functions to tie the query form to the database operation.... it's a long story). If I do so on the client, I'll have to publish the entire Vouchers collection with correct user_id fields, and although I won't be showing those ids through any templates, they would be available through the console.
Is there an inherent risk in publishing all of the IDs like this? Can they be used maliciously even if I don't leave any specific holes for them to be used in?
First, in general it sounds like a bad idea to publish all user_ids to the client. What would happen if you have 1 million users? That would be a lot of data.
Second, in specific, we cannot know if there is inherent risk in publishing your user_ids, because we do not know what could be done with it in your system. If you use a typical design of user_ids chosen by the user themselves (for instance email), then you MUST design your system to be safe even if an attacker has guessed the user_id.
Short Version: not so good idea.
I have a similar setup up: user can sign-up, if she knows the voucher code. You can only publish those vouchers where the user_id is identical to the logged in user. All other checks like "does the user input correspond to a valid voucher?" must be handled on the server.
Remember: client code is not trusted.

Resources