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

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

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.

User redirect and authentication with middleware of Nuxt

I'm trying to redirect a user to a login page if the user is not logged in when he tries to access certain pages with the following code.
// middlware/authenticated.js
import firebase from 'firebase'
export default function ({ store, redirect }) {
let user = firebase.auth().currentUser
store.state.user = user //this doesn't work
if (!user) {
console.log('redirect')
return redirect('/login')
}
}
However, the problem is with this code when I refresh a page I'm redirected to login page although without using the middleware, I can stay in the same page with logged in. For some reasons, which I don't know why, firebase can't work in middleware.
How should I modify this middleware or implement this function?
Thanks.
//middleware/authenticated.js
export default function ({
store,
redirect
}) {
if (!store.getters['index/isAuthenticated']) {
return redirect('/login')
}
}
//post.vue
async mounted () {
if (process.browser) {
let user;
if (!this.user) user = await auth(); // this auth is a plugin
await Promise.all([
this.user ? Promise.resolve() : this.$store.dispatch("setUser", { user: user || null })
]);
this.isLoaded = true;
}
},
//plugins/auth.js
import firebase from '~/plugins/firebase'
function auth () {
return new Promise((resolve, reject) => {
firebase.auth().onAuthStateChanged((user) => {
resolve(user || false)
})
})
}
export default auth
By default Firebase persists the users logged in status on successful authentication. This example uses the session, to store the user uid and cookies to store the users token and used in situations where the sessions has ended (example when browser is closed) and then a new session started but where the user is still authenticated according to Firebase. In cases like these the user will not need to sign in to view protected resources.
Your basic Middleware to protect it should look like this (if you have a Store Module called User)
export default function ({ store, redirect }) {
if (!store.getters['modules/user/isAuthenticated']) {
return redirect('/auth/signin')
}
}
In your main Store you use the ServerInit Function to get the User if there is one saved in the Cookies and load it into your User Store Module which will be used for verification in the Middleware.
Your User Store Module should look like this, and keep in mind that you remove the Cookie when you Log the User out so that he is fully logged out.
I used the things i mentioned above as the beginning of my Authentication and modified it a bit, which you can also do. Most of the credit goes to davidroyer who has set up this nice Github Repo which includes all needed files as a good example on how to accomplish your goal.

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

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:

In meteor how to verify user password before running a method? [duplicate]

There are some irreversible actions that user can do in my app. To add a level of security, I'd like to verify that the person performing such an action is actually the logged in user. How can I achieve it?
For users with passwords, I'd like a prompt that would ask for entering user password again. How can I later verify this password, without sending it over the wire?
Is a similar action possible for users logged via external service? If yes, how to achieve it?
I can help with the first question. As of this writing, meteor doesn't have a checkPassword method, but here's how you can do it:
On the client, I'm going to assume you have a form with an input called password and a button called check-password. The event code could look something like this:
Template.userAccount.events({
'click #check-password': function() {
var digest = Package.sha.SHA256($('#password').val());
Meteor.call('checkPassword', digest, function(err, result) {
if (result) {
console.log('the passwords match!');
}
});
}
});
Then on the server, we can implement the checkPassword method like so:
Meteor.methods({
checkPassword: function(digest) {
check(digest, String);
if (this.userId) {
var user = Meteor.user();
var password = {digest: digest, algorithm: 'sha-256'};
var result = Accounts._checkPassword(user, password);
return result.error == null;
} else {
return false;
}
}
});
For more details, please see my blog post. I will do my best to keep it up to date.
I haven't done this before, but I think you will need something like this on your server
Accounts.registerLoginHandler(function(loginRequest) {
console.log(loginRequest)
var userId = null;
var username = loginRequest.username;
// I'M NOT SURE HOW METEOR PASSWORD IS HASHED...
// SO YOU NEED TO DO A BIT MORE RESEARCH ON THAT SIDE
// BUT LET'S SAY YOU HAVE IT NOW
var password = loginRequest.password;
var user = Meteor.users.findOne({
$and: [
{username: username},
{password: password}
]
});
if(!user) {
// ERROR
} else {
// VERIFIED
}
});
then you can call this function from the client side like this:
// FETCH THE USERNAME AND PASSWORD SOMEHOW
var loginRequest = {username: username, password: password};
Accounts.callLoginMethod({
methodArguments: [loginRequest]
});
I have a project on github for different purpose, but you can get a sense of how it is structured: https://github.com/534N/apitest
Hope this helps,
I have found the best way to validate the users password is to use the Accounts.changePassword command and
pass in the same password for old and new password. https://docs.meteor.com/api/passwords.html#Accounts-changePassword
Accounts.changePassword(this.password, this.password, (error) => {
if(error) {
//The password provided was incorrect
}
})
If the password provided is wrong, you will get an error back and the users password will not be changed.
If the password is correct, the users password will be updated with the same password as is currently set.

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;
});

Resources