Firebase Authentication Server Side - javascript

I am trying to learn how to make a simple authentication application. If I have this in my app.js file,
var logIn = function(req, res) {
const email = req.body.email;
const password = req.body.password;
firebase.auth().signInWithEmailAndPassword(email, password).then(function() {
const user = firebase.auth().currentUser;
const emailVerified = user.emailVerified;
console.log("Email verified status: " + emailVerified);
});
};
var doSomething(req, res) {
var user = firebase.auth().currentUser;
// do something with the variable user
}
this is not ok, right? Because when multiple users log in, the currentUser would just be assigned to the last person who logged in. (Please correct me if that is not true.)
So, I found these:
How to authenticate a firebase user from server without client side auth?
https://firebase.google.com/docs/auth/web/auth-state-persistence
They suggested doing something like this:
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE)
.then(function() {
return firebase.auth().signInWithRedirect(provider);
});
Does this mean, that adding setPersistence = None allows us to use firebase.auth().currentUser on the server-side, AKA in app.js, even when multiple users are logged in?
Can we just declare firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE) once in the app.js file or do we need to use promise for each function?
I recently started learning JS and using Firebase. Sorry if some questions are silly. Thanks so much for your help.

Related

How do I convert a user authenticated with an EmailLink to a user authenticated with Email and Password?

I am using the Capacitor-Firebase/authentication plugin in my application.
My goal
I am trying to turn an Anonymous user into a user with either credentials (email/password).
Also, I want to have his e-mail verified.
My approach
My approach was to use the EmailLink functionality and then trying to convert it to a user with email and password like the following:
I have successfully signed in a user using the following code from the docs:
const signInWithEmailLink = async () => {
const credential = EmailAuthProvider.credentialWithLink(email, emailLink);
const auth = getAuth();
const result = await signInWithCredential(auth, credential);
return result.user;
};
Later, I want to convert this to a user with email and password.
I am trying to do this with FirebaseAuthentication.updatePassword({ newPassword: password.value }).
My Problem
This does not work because IMO after using signInWithCredential the state of the user is different in the native layer than in the web layer. In the native layer the user is still an Anonymous user so FirebaseAuthentication.updatePassword() won't work.
I also tried the following to try to associate a username/password:
const credential = EmailAuthProvider.credential(email.value, password.value)
const auth = getAuth()
// https://firebase.google.com/docs/auth/web/account-linking
linkWithCredential(auth.currentUser, credential)
This fails with the following error: auth/provider-already-linked.
I finally found the solution myself:
An EmailLink account is just a flavor account of an Email/Password-Account which does not have a password.
So, if I am in Capacitor, I have to do the following to add the password:
const auth = getAuth()
updatePassword(auth.currentUser!, password)
On the web, the following command works:
FirebaseAuthentication.updatePassword({ newPassword: password })

How can i seperate two types of users in firebase to seperate the login of web and mobile app users

I am currently working on a project where the parking lot owners should use website login and other users should use mobile app login.
But currently any user can login into both of the website and mobile app.
here is my firebase realtime database
my realtime database
So as you can see I defined type in user. when signing up a user gets a type depending on the device he/she registering
and my web sign in function is like this:
signInWithEmailAndPassword(auth, email, password).then((userCredential) => {
const user = userCredential.user;
alert('User Logged in!');
window.location = 'user.html';
}).catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
alert(errorMessage);
});
How can I provide login for the users which have 'type = web' ?
Firebase Authentication only cares about credentials: if the email/password you enter matches the data in the system, you can sign in - no matter what platform you're on. There is no way to change this in Firebase Authentication, so any additional logic will have to come from your application code.
For example, you could store a list of the UIDs of the parking lot owners, and check against that after signing in to allow then to use the web app or not.
signInWithEmailAndPassword(auth, email, password).then((userCredential) => {
const user = userCredential.user;
if (user) {
const uid = user.uid; // determine the UID of the user
const ownersRef = firebase.database().ref("parkinglotOwners");
const userSnapshot = await ownersRef.child(uid).get(); // try to load this users data from parkinglotOwners
if (userSnapshot.exists()) { // if this data exists
window.location = 'user.html'; // send them to the web app
} else {
alert("You're not allowed to use this app"; // tell them to go away
}
}
...
}).catch((error) => {
Firebase Auth is about authentication (are you the person you said you are).
Your need is more about Access Control. There is a feature in Firebase that may help with that. It's called "custom claims" and allow you to perform Claim-Based Access control.
see this video: https://www.youtube.com/watch?v=3hj_r_N0qMs

How to get different user in firebase auth

I am working on an app, and part of the functionality of the app is an admin being able to sign in and reset other users' passwords. How do I get a user other than the one currently signed in?
resetForm.addEventListener('submit', (e) => {
e.preventDefault();
let newPass = resetForm['reset-password'].value;
let firebaseUser = auth.user(docId);
console.log(firebaseUser);
});
I understand that that code won't actually reset any password, I just wrote that to see if I could get a user other than the one already signed in. I ran it, but instead of seeing the User UID in the console I saw this:
Uncaught TypeError: auth.user is not a function
Edit:
I have created a cloud function. Here is the code of my index.js file:
var functions = require('firebase-functions');
var admin = require("firebase-admin");
var serviceAccount = require("/removed-for-privacy.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://troop-30-elections-web-app.firebaseio.com"
});
exports.resetPassword = functions.https.onCall((docId,newPass) => {
console.log("step 2");
admin.auth().updateUser(docId, {
password: newPass,
}).then(() => {
const modal = document.querySelector('#modal-reset');
M.Modal.getInstance(modal).close();
resetForm.reset();
});
});
Here is the code that runs when the button is pressed:
resetForm.addEventListener('submit', (e) => {
console.log("Step 1");
e.preventDefault();
let newPass = resetForm['reset-password'].value;
const resetPasswordFunction = firebase.functions().httpsCallable('resetPassword');
resetPasswordFunction(docId,newPass);
console.log("Step 1.5");
});
When I open the console, there are no error messages. I see step 1 and step 1.5. Nothing else.
There is no way to identify an application administrator in Firebase Authentication. You will have to build your own infrastructure for this.
Typically this means:
In a trusted environment (like a server you control, or Cloud Function) you use the Admin SDK to change their password.
You then wrap that code into a custom API, that you expose to your application.
You call the API from your application, passing along the ID token of the signed in user.
In the API you verify the ID token, and make sure the caller is authorized for the operation.
You are completely on your own here though, as you can't use Firebase's built in password reset mechanism. That is only available to the signed-in user on the client, to prevent it being abused to spam users.

Angular 8/Nodemailer - Contact Form function not logging errors, no email received

I'm using an Angular 8/Firebase stack and I have a contact form that writes to my Firestore collection. This works fine. I have also written a cloud function that triggers on write of the database and fires off a nodemailer email to my personal email.
The issue is that I never get any emails even though the cloud function logger seems to be going off every time I submit a test contact form. No errors logged, but none of my console.logs get recorded either.
I've allowed less secure apps and disabled captchas on the Gmail I'm using to send email (even though I don't use 2FA). Still nothing. Now, I'm at a loss as to what could be going on since I have no logs to work with.
Here's my cloud function index.js:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const nodemailer = require('nodemailer');
const smtpTransport = require('nodemailer-smtp-transport');
const gmailEmail = encodeURIComponent(functions.config().gmail.email);
const gmailPassword = encodeURIComponent(functions.config().gmail.password);
const mailTransport = nodemailer.createTransport(
smtpTransport({
service: 'gmail',
auth: {
user: `${gmailEmail}`,
pass: `${gmailPassword}`
}
})
);
exports.sendContactMessage = functions.database
.ref('/messages/{pushKey}')
.onWrite(event => {
const snapshot = event.data;
if (snapshot.previous.val() || !snapshot.val().name) {
return;
}
const val = snapshot.val();
const mailOptions = {
from: `${gmailEmail}`,
to: 'donotreply#something.com',
subject: `You've been contacted by ${val.name} ✨`,
text: `${val.message}`
};
return mailTransport.sendMail(mailOptions, (error, info) => {
if (error) {
console.log('Error occurred');
console.log(error.message);
}
});
});
The trigger itself seems to be working since I see Firebase logs (but not console.logs) in the console every time I hit submit on the contact form. I think it's an issue with SMTP or my mailTransport object. I've tried quite a few different formats for this object that I found across the web but nothing.
Note: The string interpolated variables for my Gmail and password I've set via the Firebase CLI and they log correctly when I run the command to retrieve them. It's not a credentials issue.
Apparently you are mixing-up a Cloud Function trigger for the Realtime Database with a trigger for Firestore. They are two different NoSQL Database services offered by Firebase.
exports.sendContactMessage = functions.database
.ref('/messages/{pushKey}')
.onWrite(...)
is for declaring a trigger for the Realtime Database, but you indicate in your question that you write to Firestore.
So you have to change your function declaration as follows:
exports.sendContactMessage = functions.firestore
.document('messages/{pushKey}')
.onCreate((snap, context) => {...})

How can I dynamically assign a database path in Express.js based on login?

I have a backend service that I would like to use as a single point of entry for my web application, and dynamically assign a database path based on the user login.
I realize that this is not a scalable solution. I intend to use it during a testing period with several clients (accessing the ALPHA database), and also setting up a demo (accessing the SAND database).
I have the following module that I have written as a simple test to see if the login is for the demo user, all other logins will go to the other resource:
config.js
var express = require('express');
var app = express();
module.exports.dbPath = function (login){
console.log('login - ', login);
if (login === 'demo#mysite.com'){
return process.env.DB_SAND;
} else {
return process.env.DB_ALPHA;
}
};
My question is, how can I manage each unique login and assign a globally accessible reference for that session to direct each user session consistently to the correct database?
Am I overcomplicating this? If there is a different approach that would be a better practice I would welcome a suggestion in another direction.
I would use it as a middleware, and attach it to the req object for each user, something similar this:
module.exports = {
dbPath: function(req, res, next){
var login = req.body.login;
console.log('login - ', login);
if (login === 'demo#mysite.com'){
req.dbPath = 'DB_SAND';
} else {
req.dbPath = 'DB_ALPHA';
}
next();
}
};

Categories