I am using email & password authentication to logging in user, from my firebase dashboard i have set session expiration time to 2 months . However when i am closing my app from background and then after reopening of app i am getting var user = ref.getAuth(); as null
Does firebase does't take care of this? How to keep user logged in for a long period of time?
Below is the piece of code i am using to login user. I am using react-native
ref.authWithPassword({
email : 'username',
password : 'password'
}, function(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
navigatorReference.push({name:'myFeed'})
console.log("Authenticated successfully with payload:", authData);
}
});
Firebase should take care of this. Double check your configuration in the Login & Auth tab in your App Dashboard to make sure you have that setup properly.
You could also try passing along the configuration like so...
ref.authWithPassword({
email : 'username',
password : 'password'
}, function(error, authData) { /* Your Code */ }, {
remember: "default"
});
Related
In my project , user can login in system . If users do not choose "Remember me " option in beginning, their session will end after page closed entirely.
My firebase works Client Side
in firebase.js
export default ({ store, $storage }) => {
onAuthStateChanged(auth, async user => {
if (user && !user.isAnonymous) {
store.commit('firebase-auth/setUser', user);
if (user.emailVerified) {
$storage.setCookie('firebase_token', user.accessToken);
}
} else {
enableGoogleOneTapSignIn(user);
}
});
};
in MiddleWare auth.js
export default ({ $storage, req, redirect }) => {
if (process.server) {
const token = !!req.headers.cookie.firebase_token;
if (!token) return redirect('/');
} else if (process.client) {
const token = !!$storage.getCookie('firebase_token');
if (!token) return redirect('/');
}
};
This code controls if user signed in or not . This works in server and client side.
The problem is ; When user chose "remember me" option at the beginning , session will not finish even if user closes entire browser. And if user opens the browser again , his session will continue and he will be logged-in already but in this scenario I can not reach cookie because it was deleted when browser was closed .
If user comes with a direct link like abc.com/user/my-profile when browser was closed, user will not reach his profile beacuse cookie was cleaned . I need to find a good way for this problem .
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.
The Firebase authListener shows the account chooser but doesn't recognise any user the first time I try to login.
Then, trying to login again for a second time, FirebaseUI skips the account chooser and immediately redirects back, after which the Firebase authListener does recognise the user. The same is true for the Google account chooser as for "Sign in with email" and choosing the same Google address.
This problem makes all my users need to press the login button twice. Once for the account chooser and a second time to actually login with the user now recognised.
Here is my build:
Firebase initialisation
firebase.initializeApp(config.firebase)
firebase.auth().onAuthStateChanged(user => {
if (user) {
return console.log('found this user! ', user)
}
console.log('no user found during authListener!')
})
firebase.auth().getRedirectResult()
.then(result => { console.log(result.user) })
.catch(error => { console.log(error) })
Here is what happens when the login page is mounted
let ui = firebaseui.auth.AuthUI.getInstance()
if (!ui) {
ui = new firebaseui.auth.AuthUI(firebase.auth())
}
ui.start('#firebaseui-auth-container', uiConfig)
Here is my config:
uiConfig = {
signInSuccessUrl: '/',
signInOptions: [
{
provider: firebase.auth.EmailAuthProvider.PROVIDER_ID,
requireDisplayName: false
},
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
],
tosUrl: 'localhost'
}
Versions:
"firebase": "^5.0.4",
"firebaseui": "^3.0.0",
PS:
My website is an SPA
I "solved" this problem by adding the following to the config object:
credentialHelper: firebaseui.auth.CredentialHelper.NONE
This turns off the account chooser entirely, instead prompting users to type in their email address, resulting in a much nicer user experience IMHO compared to the ugly and confusing account chooser.
If I have a user that logged in with email/password is there any way to change that user’s email address on the backend?
I see I can have the user change it themselves with oldEmail, newEmail, password:
ref.changeEmail({
oldEmail : "bobtony#firebase.com",
newEmail : "bobtony#google.com",
password : "correcthorsebatterystaple"
}, function(error) {
if (error === null) {
console.log("Email changed successfully");
} else {
console.log("Error changing email:", error);
}
But is there any way for me to change it for them without the password?
From Firebase support: There is no way to change the user's email address without the password.
I sent enrollment email to the user and when he enters password and other details I'm trying to reset the password but it is throwing error
uncaught error extpected to find a document to change
As you can see in the mage
I've subscribed to the user record
my code
this.route('enroll', {
path: '/enroll-account/:token',
template: 'enroll_page',
onBeforeAction: function() {
Meteor.logout();
Session.set('_resetPasswordToken', this.params.token);
s = this.subscribe('enrolledUser', this.params.token).wait();
}
}),
After I'm displaying form and on the submit event
onSubmit: function(creds) {
var options = {
_id: Meteor.users.findOne()._id,
name: creds.name
}
var token=Session.get('_resetPasswordToken');
Meteor.call('updateUser', options, function(error, result) {
if(!error) {
Accounts.resetPassword(token, creds.password, function(error) {
if (error) {
toastr.error("Sorry we could not update your password. Please try again.");
return false;
}
else{
toastr.error("Logged In");
Router.go('/');
}
});
} else {
toastr.error("Sorry we could not update your password. Please try again.");
return false;
}
});
this.resetForm();
this.done();
return false;
}
Everything is working fine but resetpassword callback is not triggering and the above error is displaying in console.
my token is get deleted from the user record and I'm able to login using login form but
From the docs
Reset the password for a user using a token received in email. Logs the user in afterwards.
I'm not able to automatically login after resetting the password,above error is throwing
What am I missing here?
this.subscribe('enrolledUser', this.params.token).wait();
here you're subscribing using resetPassword token
when you call Accounts.resetPassword method the method will reset the password and delete the token from user record.
So your subscription is lost and there are no records available in client side to modify
(That is waht the error Expected to find a document to change)
Instead on first subscription save the user Id and subscribe to the user record using Id
so the subscription will not be lost
path: '/enroll-account/:token',
template: 'enroll_page',
onBeforeAction: function() {
Meteor.logout();
Session.set('_resetPasswordToken', this.params.token);
s = this.subscribe('enrolledUser', this.params.token).wait();
},
onAfterAction:function(){
if(this.ready()){
var userid=Meteor.users.findOne()._id;
Meteor.subscribe("userRecord",userid);
}
}
Alternatively, you could do something like as follows in your publication. This worked for me (but mine was a slightly more involved query than this).
Meteor.publish('enrolledUser', function (token) {
check(token, String);
return Meteor.users.find({
$or: [{
_id: this.userId
}, {
'services.password.reset.token': token
}]
});
});
From the docs, it says
Reset the password for a user using a token received in email. Logs the user in afterwards.
So basically, you have to subscribe to the logged in user after the fact as well. A little silly, but whatever.