Dynamic Group Security in Firebase Storage - firebase

I've reviewed the documentation for Group Security for Firebase Storage where it suggests to use custom tokens or including group information in file metadata, however, when a user leaves a group, I don't ideally want to be updating the metadata in every file or having to create a new folder and update the group token information.
If this were the Realtime Database, I'd check to see if the user was still a group member. Is there a more elegant solution along these lines that I can explore?
Many thanks

yeah with custom tokens you can associate additional attributes with the user. The trick is when you create custom token for a given user id, you can add additional claims and use them to evaluate the access rules. In your case you can add the group id to which the user belongs as additional claim when the token is created on your server using which the user logs-in from the client app. So whenever the user leaves your group, his group id association is removed and hence when you recreate the token for that user, you will no more associate the group id in additional claims.
Sample Example
public static String createCustomToken(String userId, Map<String, Object> additionalClaims) {
FirebaseOptions options = new FirebaseOptions.Builder()
.setServiceAccount(new FileInputStream(FIREBASE_ACCESS_FILE)).build();
FirebaseApp.initializeApp(options);
Task<String> customToken = FirebaseAuth.getInstance().createCustomToken(userId, additionalClaims);
return customToken.getResult().toString();
}

Related

How do I implement a follower system with private accounts in Firebase?

I'm building an an app in Firebase with a user feature and I need to implement a system to allow:
A user to follow another user
A user to see a list of the users they're following
A user to set their profile as private so that some of their data is only visible to the people following them
A user to be able to send a follow request to a user with a private profile
A user with a private profile to be able to accept/reject follow requests
So far I've made a Firestore collection at the root called users. When a user signs up with Firebase Auth, a document is made in users with the following structure:
user (document)
username: stringaccountIsPrivate: boolean
userData (collection)
userData (document)
where all the data that would be hidden if the account were private is in the userData document.
I'm not sure how I could implement the system to fulfill my requirements from here so that I could use Firestore rules to only allow followers of a private account to view that account's userData. I would appreciate it if anyone could suggest an appropriate data structure and an outline of how to write rules for this.
For this kind of situation, you must maintain two sources of truth, one for the creator and one for the user. this is done with an array of strings in both that have the user_uid and any additional information concatenated.
The goal is to have an array of CSV-like values of which you can split and render within your app.
create a concat string: entry = [user.uid, user.name, user.url].join(';');
return string to object: entry.split(';');
Doing the following ensures that only a unique entry exists
db.doc("user/user_id/userData/followers").set({ followers: Firestore.FieldValue.ArrayUnion(entry)}, {merge: true});
This is only a rough example and some backend logic will be needed to scale large - but with this, you have a theoretical limit of 200k entries depending on how much data you want to store in the string.
Additional logic would involve cloud functions reading and writing when a request to follow has been created which handles a counter that creates new documents as needed and ensure's that the counter is updated to prevent overflow since Security Rules can't do any recursive logic or manipulate the request directly.

How to set Firebase Custom DocID using Dart

I'm using Flutter/Dart in my app, I can register users with de next sentence
Firestore.instance.collection('users').add(toMap(user));
but Firebase, creates the document with an automatic id, I want to create the document with the username to perfom the queries based on the username. (Find the products using the username)
I tried set the id in the User DTO but Firebase save it with automatic id.
Use the user's id to create the document like this
Firestore.instance.collection('users').document(user.uid).setData(toMap(user));

How to restrict access to files to group members?

I have a chat app where users can send photos in private or group chats. Each private or group chat has a unique chat id: /images/<chat id>/image.jpg
How would one secure the access to this files so that only the chat members can view them? In the Firebase database I have a node with a structure like: /members/<chat id>/member1: true.
Is this actually necessary, since the links are only posted to the corressponding chats?
Can any authed user actually browse through the files saved in the Firebase storage? Or is this prevented by design?
The eternal question. It's discussed a few places (Google Group, Storage Docs, Github Gist), but the TL;DR is: at present, there's no way to read data from one service in the Rules of another. For services, you can do one of two things:
Convey group information in a custom token
Convey group information in custom metadata in the service
One example of this:
// Allow reads if the group ID in your token matches the file metadata's `owner` property
// Allow writes if the group ID is in the user's custom token
match /files/{groupId}/{fileName} {
allow read: if resource.metadata.owner == request.auth.token.groupId;
allow write: if request.auth.token.groupId == groupId;
}

Getting uid after createUser()

With iOS/MacOS is there a way to retrieve the uid immediately after createUser?
Our app allows an Admin user to create other user accounts (email/password authentication) and also stores some other info about the created user in our Firebase with the uid as the key.
One solution is when the admin creates the user (createUser), it's followed by authenticating the user, grabbing the uid from FAuthData that's returned and then create the key/value pair in our Firebase with the user data. Then that account is unauth'd.
In doing a Google search, there were a couple of references to something like this
-(void)createUser:(NSString *)email password:(NSString *)password withCompletionBlock(NSError *error, User *user) {block}
where it appears a user (or FAuthData?) was returned.
If no, what is the best practice to get the just-created uid? Is this a feature that is upcoming?
2/22/2016 update
For completeness, soon after I asked this question, Firebase was updated with a method that created the user and returns the uid at the time of creation:
createUser:password:withValueCompletionBlock:
- (void)createUser:(NSString *)email password:(NSString *)password
withValueCompletionBlock:(void ( ^ ) ( NSError *error, NSDictionary *result ))
The result dictionary contains newly-created user attributes, including uid.

Is there any benefit to storing user information in AspNetUserClaims with Asp.Net Identity 2?

I would like to store some additional user information. From what I understand the following is the usual option:
public class ApplicationUser : IdentityUser {
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager) {
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public string FirstName { get; set; }
public string LastName { get; set; }
}
Here the FirstName and LastName have been added and they will appear as additional fields in the AspNetUsers table.
However it seems that now with Asp.Net Identity there's also an option to store this type of information in the AspNetUserClaims table.
Can someone explain to me. Going forward is this the kind of information that could be stored in AspNetUserClaims. If so then does anyone have any examples of this.
At the end of the day, your signed in user will be converted into a series of claims stored in the ClaimsIdentity representing your user in HttpContext.User.Identity. You can choose to store FirstName/LastName as columns in the user table which you then can explicitly read out and convert into the appropriate claims (if desired), or you can store them directly as claims in the AspnetUserClaims table (which is just stores them as two string columns) which by default will just automatically get added to your user's claims identity. Both methods are more or less equivalent though, so its up to personal preference.
BTW the only reason you would want these in the user's ClaimsIdentity at all, is if you wanted to save a db hit just to display the name, and always use the FirstName/LastName claims in the ClaimsIdentity. If you fetch the user, and use user.FirstName instead, there isn't much value in also generating the name claims.
In addition to #Hao Kung, when claims are going to be longer than allowed Cookie capacity of the browser, claims information could be trimmed.
According to Thinktecture Identity Server article, one of the famous alternative of default AspNet Identity, it said as below.
Once your application becomes complex, so are the number of claims to handle. By default, all the claims are stored as part of the session cookie and browsers like Safari impose a restriction on the size of the cookie. So one fine day, when you add few more claims to the application, you will start getting serialization errors. That's because only partial cookie will be sent back to the server and server does not know what to do with it. So the solution for this problem is to create the security token in "Reference" mode. What it means is to store the token on the server and just store a reference session id as the cookie. See the image below. The cookie size is just few bytes:
When saving information (a claim) in the ApplicationUser, every user will have a placeholder (a column in the DB) for this information, and you will be able to save 0 (null) or 1 value. Should a new type of information be required, you would need a new column.
On the other hand, saving information (a claim) in the AspNetUserClaims table means the information is saved only when it is needed, that you can have multiple claims of the same type and that you can add new claims at any time without touching the data model.
Examples:
Let's think of a claim is_top_1percent_of_whatever. If underlying data is saved in ApplicationUser, 99% of the users will have a null or false entry.
For a claim high_school_name, if it is saved in ApplicationUser you can record only 1 high school name... if the user went to 2 different schools, there is no way of record this information. If saved in AspNetUserClaims, you can record as many high school per user as needed.
Related to the previous example, let's say you spell out explicit claims like is_interested_in_sport, is_interested_in_camping. The day you want is_interested_in_fishing, you would have to change the DB model is stored in ApplicationUser or just add a record if stored in AspNetUserClaims. This is also true for persisting claims received from an external provider.

Resources