Save data into the Firebase user object - firebase

Using this :
firebase.auth().onAuthStateChanged(function(user){
if (user){
Firebase is returning the user, which includes a userID.
When a webpage get this object it takes the user ID, and need to access again the DB records to check some very basic stuff ( if it's a paid user, or another simple key) in order to choose the UI.
Is there away to return in this callback the user including some predefined basic info about him?

If you want to add some extra info to a user account you cannot use the User object, which has a fixed list of properties.
There are two classical approaches:
1. Create a specific User record
You create a Firestore document (or a Realtime Database node) for each user. Usually you use the User's uid as the id of the Firestore Document/RTDB node.
This means that you will need to fetch the database to get this extra user info, e.g.
firebase.auth().onAuthStateChanged(function(user){
if (user) {
firebase.firestore().collection('users').doc(user.uid).get()
.then(...)
}
//...
}
2. Use Custom Claims
If you need to store only a limited number of extra information in order to provide access control, you could use Custom Claims. This is particularly adapted for storing user roles, e.g. paid user.
Custom user claims are accessible via user's authentication tokens, therefore you can use them to modify the client UI based on the user's role or access level. See more details here in the doc.
To get the Claims in the front end, you can do as follows:
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
user.getIdTokenResult()
.then((idTokenResult) => {
// Confirm the user is a paid user.
if (!!idTokenResult.claims.paidUser) {
// Show admin UI.
showPaidUserUI();
} else {
// Show regular user UI.
showRegularUI();
}
})
.catch((error) => {
console.log(error);
});
}
//...
}
It is important to note the following section in the doc:
Custom claims are only used to provide access control. They are not
designed to store additional data (such as profile and other custom
data). While this may seem like a convenient mechanism to do so, it is
strongly discouraged as these claims are stored in the ID token and
could cause performance issues because all authenticated requests
always contain a Firebase ID token corresponding to the signed in
user.
Use custom claims to store data for controlling user access only. All other data should be stored separately via the real-time database
or other server side storage.
Custom claims are limited in size. Passing a custom claims payload greater than 1000 bytes will throw an error.

Related

How to get user email using firebase analytics event parameter?

I'm making app using firebase on back-end part. User can log in myApp using google account and can backup their data.
I want to delete some doc at cloud when user uninstall myApp.
collection ID is user email.
So I need to know user email but I don't know how to get this.
exports.appUninstall = functions.analytics.event("app_remove").onLog((event)=>{
const userEmail = event.user.email;
database.doc(userEmail + "/user_logged_in").delete();
return console.log("Deleted user_logged_in because user uninstalled app");
});
I don't think this is possible with the structure you currently have. As you can see here, the onLog() returns a AnalyticsEvent object that has a UserDimensions object inside of it, however, that object stores the userId and an object with the userProperties.
I can' t say for sure that this userProperties will store the email of your user so you can fetch in firestore, so you need to test this. If not, you will have to find a way get the user document with it's userId, maybe by adding it to the document itself on creation.
This would be easier if you changed the documentId to be the userId instead of the email, if you do that you can simply use the mentioned userId of the event to get the document and delete it.

How to set different custom claims based on where user signs up?

I have 2 sign up pages, one for students and one for teachers.
How can I set a different custom claim for each when they sign up? (student: true OR teacher:true)
I am guessing it could be possible at .onCreate? I can set just one custom claim per onCreate function, how to make this dynamic?
exports.AddTeacherRole = functions.auth.user().onCreate(async (authUser) => {
if (authUser.email) {
const customClaims = {
teacher: true,
};
The authUser Object passed to Authentication Cloud Functions is exactly the same type of Object than the UserRecord that is returned by the Firebase Admin SDK.
Depending on how you created the user (e.g. from the front-end, with createUserWithEmailAndPassword() in JavaScript or e.g. from the Admin SDK, with createUser()) the UserRecord may hold more or less "extra" properties (e.g. displayName, photoURL, etc).
The problem is that there is no specific property of this UserRecord instance that you could use to indicate the role of the user (student or teacher). Therefore you cannot get this information in your Cloud Function, which is triggered when the user is created.
A common approach is to add this kind of extra data to a Firestore document (or a Realtime Database node) which has the same id than the user Id. Then from this document, you could trigger a Cloud Function that updates the user record.
Another approach is to use a Cloud Function that does all the job: create the user, set the custom claim and create a Firestore document. You will find in the following article some detailed explanations about this kind of approach.
To answer the question you asked in the deleted answer:
Just to verify, it could be solved by creating 2 separate https
callable cloud functions that handle the sign up for each role for
example?
Yes you could have two callable Cloud Functions, but I think it should be possible to just have one and pass different parameter values when calling this Cloud Function, depending on the user role.
Something like:
const setCustomClaim = firebase.functions().httpsCallable('setCustomClaim');
const userRole = "student" // or role = "teacher"
setCustomClaim({role: userRole, userId, bar: "foo" }).then(function(result) {
// Read result of the Cloud Function.
// ...
});

Firebase - Adding properties to authenticated user [duplicate]

I'd like to add a property to a Firebase user object. The user documentation says that I can only store additional properties using the Firebase real time database.
I am unsure on how this can works in practice.
What does the following mean in practice?
You cannot add other properties to the Firebase User object directly;
instead, you can store the additional properties in your Firebase
Realtime Database.
I interpret it as following:
"you cannot modify properties of a FIRUser object but you can combine this with additional objects"
I found the set function documentation which I interpet in this way:
var userRef = ref.child("users");
userRef.set({
newfield: "value"
});
Is this a sensible approach?
You're almost there. In the legacy Firebase documentation, we had a section on storing such additional user data.
The key is to store the additional information under the user's uid:
let newUser = [
"provider": authData.provider,
"displayName": authData.providerData["displayName"] as? NSString as? String
]
// Create a child path with a key set to the uid underneath the "users" node
// This creates a URL path like the following:
// - https://<YOUR-FIREBASE-APP>.firebaseio.com/users/<uid>
ref.childByAppendingPath("users")
.childByAppendingPath(authData.uid).setValue(newUser)
I've added a note that we should add this information in the new documentation too. We just need to find a good spot for it.
According to the Custom Claims documentation,
The Firebase Admin SDK supports defining custom attributes on user accounts. [...] User roles can be defined for the following common cases:
Add an additional identifier on a user. For example, a Firebase user could map to a different UID in another system.
[...] Custom claims payload must not exceed 1000 bytes.
However, do this only for authentication-related user data, not for general profile information, per the Best Practices:
Custom claims are only used to provide access control. They are not designed to store additional data (such as profile and other custom data). While this may seem like a convenient mechanism to do so, it is strongly discouraged as these claims are stored in the ID token and could cause performance issues because all authenticated requests always contain a Firebase ID token corresponding to the signed in user.
Use custom claims to store data for controlling user access only. All other data should be stored separately via the real-time database or other server side storage.

Create OAUTH accounts prior to signin

I have an app I am working on that has one or two "main" accounts linked to other accounts. I am using accounts-password, accounts-google, and alanning:roles currently, but I may add other OAuth providers later.
The idea is that there will be one or two users that are "admins" for a larger group, and as such I'd like to be able to allow them to add users. I don't mind if I have to go through the OAuth authorization page on first login, but I would like to be able to add the users to the Meteor.users collection and allow the admins to set things up prior to their logging in, but I am not sure how to do it at all. Can I simply add a user to the Meteor.users collection with only the email attribute of the google sub-document populated? I would guess that the email is NOT what Meteor uses to connect a stored Meteor.users document to the corresponding Google OAuth account as there is also an id attribute that seems like it might be more useful for that purpose.
So based on what you say on the comment i elaborate this DEMO, since you want to create users and add that users to some kind of "role" or "group" im using here the meteor-roles package.
allow people to create a group of users
This could be done on some differentes ways, for the demo purpose im creating 1x1 user and leater assign them a Role.
to avoid the autologin behavior you should use a Meteor.method.
//server
Meteor.methods({
createSimpleUser:function(email){
return Accounts.createUser({
email:email,
password:"test123" //you can force the user in the first login to change the password.
})
}
})
//Client
Meteor.call('createUser',"test#gmail.com")
Now that you have the user created, you should assign them a role.
So on some event handler you want,do something like this.
//this should be incide an {{#each}} or {{#with}} in order to this._id works, if not use Sessions
Meteor.users.update({_id:this._id},{$set:{group:newGroup}})
This is just the global idea, you should protect in the allow rules, who can edit the users group, you can also use template helpers like
{{if userIsInRole 'nameGroup'}}
<!-- Show content only available to users in this group -->
{{else}}
<!-- Some warning access denied template -->
{{/if}}
This process will add some stuff to the profile of the user so the app
can associate all the users together
Filter by groups.
Template.example.helpers({
groupX:function(){
return Meteor.users.find({group:"X"})
}
})
when they first login without erasing the stuff I setup for them prior
to them signing in.
Here again there could be many reasons, for example you can add some field inside the current user created.
Meteor.methods({
createSimpleUser:function(email){
return Accounts.createUser({
email:email,
password:"test123",
firstLogin:false //by default the users login is false.
})
}
})
and in the iron route, create a function(why on the router? see Overworked helpers on the David Weldon webpage).
requireEdit = function(){
var query = Meteor.users.findOne({_id:this.params._id})
if(query.firstLogin == false){
this.render('profileEditin') //do some change password here and other stuff
}else{
this.next()'
}
}
And call it on the onBeforeAction() method
This is just the idea and demo is a ugly-fast example of how it should work, good luck

Authorization in ASP.NET Web API: Return data specific to authorized user

Let's assume I implemented token based authorization with a custom filter attribute as described here.
Let's also assume, I have a controller that returns tasks:
public IEnumerable<Task> Get()
{
// return tasks for authorized user
}
Now, how would I go about returning only the tasks for the authorized user? Passing the user ID as a query parameter is not an option - it is forbidden to request the tasks of a different user.
you could enrich the HttpRouteData from your action filter and read it in the controller action. actionContext.ControllerContext.RouteData.Values.Add("UserId", someVaue );
You could also use the System.Runtime.Remoting.Messaging.CallContext class ( GetData and SetData )
In the code in the sample you linked to, they are encrypting the user's name in the token. In the filter they are getting this token from an http header, decrypting it back to the username, and querying it against an AuthorizedUserRepository.
AuthorizedUserRepository.GetUsers().First(x => x.Name == RSAClass.Decrypt(token));
You can certainly use a userid instead of the name, and work against a real repository instead of this sample one. You could either do all of this over again in the controller action or constructor, or you could pass it along the route data or some ThreadStatic property. If you want to get really fancy, you could implement claims based security and set a claim on the current thread's principal. Really it doesn't matter how you pass it along.
Ultimately you would just use this value in a where clause of a query or linq statement to filter down to the data you want the user to be allowed to access.

Resources