Firebase authentication (email/password) how to set user's uid? - firebase

I am authenticating using email/password like so:
firebase.auth().createUserWithEmailAndPassword(email, password).catch(function(error) {
// Handle Errors here.
});
And listening to auth here:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
console.log(user);
} else {
// No user is signed in.
console.log("Not signed in");
}
});
Which works fine. On examining the user object that auth returns, the uid is a random string, is there a way to set this uid when I create the account? For example, uid="someUserName"?
Thanks

Firebase Authentication is not like a database where you can add properties and such. It handles UID's and such for you. What you can do, is in your Firebase Database add a users directory and store additional info there (such as a username) with the UID as the key.
Here's an example:
If you're going to use this often, it's probably a good idea to go into your database rules and add an index on this username:

Related

Is there a way to generate a firebase email verification link before a user is actually signed up?

I am currently implementing a MFA system with Firebase Authentication & Google Authenticator.
Since my users are not allowed to authenticate with a non-verified email address, I'd like to prevent them from signing-in if their Firebase Authentication email_verified is set to false. To do that, I am using Google Cloud Identity Provider blocking functions, this works perfectly.
However, when it comes to the registration beforeCreate blocking function hook, I can't find a way to generate an email verification link for the user currently being created, the documentation says:
Requiring email verification on registration The following example
shows how to require a user to verify their email after registering:
export.beforeCreate = authClient.functions().beforeCreateHandler((user, context) => {
const locale = context.locale;
if (user.email && !user.emailVerified) {
// Send custom email verification on sign-up.
return admin.auth()
.generateEmailVerificationLink(user.email)
.then((link) => {
return sendCustomVerificationEmail(
user.email, link, locale
);
});
}
});
export.beforeSignIn = authClient.functions().beforeSignInHandler((user, context) => {
if (user.email && !user.emailVerified) {
throw new gcipCloudFunctions.https.HttpsError(
'invalid-argument', `"${user.email}" needs to be verified before access is granted.`);
}
});
However, as far as I understand, generateEmailVerificationLink() can only be called to generate email verification link of an existing Firebase Authentication user. At this stage (while running beforeCreate blocking function), the user is not created yet.
Now I am wondering, I am missing something or is the Google documentation wrong?
No.
User data is created upon registration in the database.
Then, you may send an Email-Verification with a link automatically.
This Email-Verification just updates the field emaiVerified of said user data.
If you want to prevent users with unverified Emails from logging in, you need to adjust your Login page and check whether emaiVerified is true.
Important: Google will sign in a user right upon registration whether the email is verified or not, as this is the expected behavior from the perspective of a user. Email verification is ensured on the second, manual login.
(Also, please do not screenshot code.)
You can let a user sign in via email link at first, and call firebase.User.updatePassword() to set its password.
I am using Angular-Firebase, this is the logic code.
if (this.fireAuth.isSignInWithEmailLink(this.router.url)) {
const email = this.storage.get(SIGN_IN_EMAIL_KEY) as string;
this.storage.delete(SIGN_IN_EMAIL_KEY);
this.emailVerified = true;
this.accountCtrl.setValue(email);
from(this.fireAuth.signInWithEmailLink(email, this.router.url)).pipe(
catchError((error: FirebaseError) => {
const notification = this.notification;
notification.openError(notification.stripMessage(error.message));
this.emailVerified = false;
return of(null);
}),
filter((result) => !!result)
).subscribe((credential) => {
this.user = credential.user;
});
}
const notification = this.notification;
const info = form.value;
this.requesting = true;
form.control.disable();
(this.emailVerified ? from(this.user.updatePassword(info.password)) : from(this.fireAuth.signInWithEmailLink(info.account))).pipe(
catchError((error: FirebaseError) => {
switch (error.code) {
case AUTH_ERROR_CODES_MAP_DO_NOT_USE_INTERNALLY.POPUP_CLOSED_BY_USER:
break;
default:
console.log(error.code);
notification.openError(notification.stripMessage(error.message));
}
this.requesting = false;
form.control.enable();
return of(null);
}),
filter((result) => !!result)
).subscribe((result: firebase.auth.UserCredential) => {
if (this.emailVerified) {
if (result.user) {
notification.openError(`注册成功。`);
this.router.navigateByUrl(this.authService.redirectUrl || '');
} else {
notification.openError(`注册失败。`);
this.requesting = false;
form.control.enable();
}
} else {
this.storage.set(SIGN_IN_EMAIL_KEY, info.account);
}
});
Mate, if database won't create a new user using his email and password, and you send him email verification which will create his account, how the heck database will know his password? If it didn't create his account in the first step? Stop overthinking and just secure database using rules and routes in application if you don't want user to read some data while he didn't confirm email address.
It is that simple:
match /secretCollection/{docId} {
allow read, write: if isEmailVerified()
}
function isEmailVerified() {
return request.auth.token.email_verified
}
I think the blocking function documentation is wrong.
beforeCreate: "Triggers before a new user is saved to the Firebase Authentication database, and before a token is returned to your client app."
generateEmailVerificationLink: "To generate an email verification link, provide the existing user’s unverified email... The operation will resolve with the email action link. The email used must belong to an existing user."
Has anyone come up with a work around while still using blocking functions?
Using firebase rules to check for verification isn't helpful if the goal is to perform some action in the blocking function, such as setting custom claims.

Make Additional Custom Checks in Email-Password Auth (Flutter - Firebase)

For a Flutter- Firebase Mobile App , is it possible to have additional checks done (while using Email/Password Auth) like appending the phone's Device ID and have it checked with the Firestore Database to ensure the Email-Device uniqueness (to prevent running same app on multiple devices with same email/password) on Signin.
Current Signin Method (https://firebase.flutter.dev/docs/auth/usage/) only accepts Email+Password and couldn't find a way to append additional user info for authentication.
signInWithEmailAndPassword() method:
try {
UserCredential userCredential = await FirebaseAuth.instance.signInWithEmailAndPassword(
email: "barry.allen#example.com",
password: "SuperSecretPassword!"
);
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
print('No user found for that email.');
} else if (e.code == 'wrong-password') {
print('Wrong password provided for that user.');
}
}
Email password authentications requires email and password only for authenticating users. If you need to store any additional data then you would have to store them in either Custom Claims or any database. You can read this information as necessary. However, users can still reverse engineer the application and sign in without that additional check since Firebase requires E-Mail and Password only and it'll still work even without your additional auth extension.
Whenever a user logs in, you can check if any session for that user account exists in database and then perform relevant actions depending on if a session exists or no.

Can't change password and login at same time in Meteor

In my Meteor app, users are invited based on their email, then proceed to set their own password. I want the users to land on the page and then set the password. From there, they will be logged into the account. I am able to set the users password, but can not log them in immediately after.
This is how I have my code:
Client Side
Meteor.call('setStudentPassword', password, id, function (error, result) {
if (!error) {
Meteor.loginWithPassword(id, password, function (error) {
console.log(error);
Router.go('studentCreditCard');
});
}
});
Server Method
setStudentPassword: function(password, id) {
if (Meteor.isServer) {
return Accounts.setPassword(id, password);
}
}
Is this possible to do, or will I need to set a temporary password on account creation?
You can't write Meteor.loginWithPassword(id [...]) according to the docs, but have to write Meteor.loginWithPassword({id: id} [...]) instead.
But more importantly: anyone can call the method setStudentPassword, and change the password for anyone. A huge security risk, I strongly advice you against using this code. Further more, the users password is sent over the wire in plain text, which should be avoided.

Firebase / AngularFire create user information

I'm creating a new user with AngularFire. But when I sign the user up I also ask for first name and last name and I add that info after registration.
$firebaseSimpleLogin(fbRef).$createUser($scope.signupData.email, $scope.signupData.password).then(function (user) {
// Add additional information for current user
$firebase(fbRef.child('users').child(user.id).child("name")).$set({
first: $scope.signupData.first_name,
last: $scope.signupData.last_name
}).then(function () {
$rootScope.user = user;
});
});
The above code works, it creates node fin Firebase (users/user.id/ ...).
The problem
When I login with the new user I get the user default information: id, email, uid, etc. but no name. How can I associate that data automatically to the user?
You can't. Firebase hides the complexity of login management by storing the login details in its own datastore. This process knows nothing of your app's forge, which means it doesn't know if or where you're storing any additional user information. It returns the data that it does know about as a convenience (id, uid, email, md5_hash, provider, firebaseAuthToken).
It's up to your app to then take the [u]id and grab whatever app specific user information you need (such as first name, last name). For an Angular app, you'd want to have a UserProfile service which retrieves the data you're looking for once you get the authentication success broadcast.
Also, in your snippet, consider changing
.child(user.id)
to
.child(user.uid)
This will come in handy if you ever support Facebook/Twitter/Persona authentication later on. uid looks like "simplelogin:1" - it helps to avoid unlikely but possible id clashes across providers.
I have the same issue on this and feel like noone actually has a clear answer (2 years on). But here is the rough structure of how such a service could look like:
app.factory('Auth', function(FURL, $firebaseAuth, $firebaseObject, $rootScope, $window){
​
var ref = new Firebase(FURL);
var auth = $firebaseAuth(ref);
​
var Auth = {
user: {},
​
login: function(user){
return auth.$authWithPassword({
email: user.email,
password: user.password
});
},
​
signedIn: function(){
return !!Auth.user.provider;
},
​
logout: function(){
return auth.$unauth;
}
};
​
// When user auths, store auth data in the user object
auth.$onAuth(function(authData){
if(authData){
angular.copy(authData, Auth.user);
// Set the profile
Auth.user.profile = $firebaseObject(ref.child('profile').child(authData.uid));
Auth.user.profile.$loaded().then(function(profile){
$window.localStorage['gym-key'] = profile.gym.toString();
});
} else {
if(Auth.user && Auth.user.profile){
Auth.user.profile.$destroy();
}
​
}
});
​
return Auth;
});

How do I remember a logged user using AngularFire and Firebase Simple Login?

This is what I'm using for Authentication:
new FirebaseSimpleLogin(new Firebase("firebaseURL"), function(error, user) {
if (error) {
} else if (user) {
// angular ngCookies service
$cookies.user = args.user.email;
} else {
}
});
To store all the todos per user, I'm simply storing his email ID against each todo. The problem with this approach is that I can modify the cookie replacing with someone else's email then I could see their todos.
Is there a way to know who has logged in using Firebase simple login instead of looking at the cookie? Is there any better way?
You can use angularFireAuth, which will bind a model to user authentication state:
function MyController($scope, angularFireAuth) {
var ref = new Firebase("https://<my-firebase>.firebaseio.com/");
angularFireAuth.initialize(ref, {scope: $scope, name: "user"});
}
$scope.user will then be null if the user is logged out, and set to a user object when the user is logged in. Learn more at http://angularfire.com/documentation.html#authentication

Resources