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.
Related
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) => {...})
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.
I am creating a React web app where the user sign in/up and other authentication related processes are being handled by AWS Cognito and the accompanying Javascript SDK.
My app has some 'public' routes/pages that everybody, signed in or not, can view, such as /documentation/ and /sign-in/. There also exist various private routes which you can only see when you are logged in, such as /my-documents/.
At the moment, I have a working sign in page, where a user is signed in with code very similar to use case #4 (Cognito Docs).
My question now is: as soon as a user goes to /my-documents/, how do I check whether the user is signed in and actually has the rights to see this page?
I am not using AWS Amplify for the authentication in my app. I only use the NPM package 'amazon-cognito-identity-js'.
This is the code I currently use to check if the session is valid, in other words if the user is successfully signed in. This however, seems like a cumbersome way to check such a simple status.
const isAuthenticated = () => {
const cognitoUser = userPool.getCurrentUser();
let isSessionValid = false;
if (cognitoUser) {
cognitoUser.getSession((err: Error, result: CognitoUserSession) => {
if (!err) {
isSessionValid = result.isValid();
}
});
}
return isSessionValid;
};
isSessionValid is returned before the callback in getSession is executed.
My Firebase web app requires administrator access, i.e., the UI should show a few things only for admins (an 'administrator' section). I came up with the below as a means to authorize the UI to display the admin section for valid admins only. My question is, good or bad? Is this a sound means of authorizing? ...so many ways to do this. This particular way requires me to configure admins in the security rules (vs in a node/tree in a db/firestore)
My idea is that if the .get() fails due to unauthorized access, I tell my app logic the user is not an admin, if the .get() succeeds my logic shows the 'admin' sections. Of course, the 'sections' are just HTML skeletons/empty elements populated by the database so even if the end user hacks the JS/logic, no real data will be there - only the empty 'admin section' framework.
function isAdmin(){
return new Promise(function(resolve, reject){
var docRef = firebase.firestore().collection("authorize").doc("admin");
docRef.get().then(function(result) {
if (result) {
resolve (true);
}
}).catch(function(error) {
resolve (false);
});
});
}
The firestore rule specifies the 'admins' by UID.
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth.uid == "9mB3UxxxxxxxxxxxxxxxxxxCk1";
}
}
}
You're storing the role of each user in the database, and then looking it up in the client to update its UI. This used to be the idiomatic way for a long time on realtime database, and it still works on Firestore.
The only thing I'd change is to have the rules also read from /authorize/admin, instead of hard-coding the UID in them. That way you only have the UID in one place, instead of having it in both the rules and the document.
But you may also want to consider an alternative: set a custom claim on your admin user, that you can then read in both the server-side security rules (to enforce authorized access) and the front-end (to optimize the UI).
To set a custom claim you use the Firebase Admin SDK. You can do this on a custom server, in Cloud Functions, but in your scenario it may be simpler to just run it from your development machine.
Detailed How To: Firebase has what's called Custom Claims for this functionality as detailed in their Control Access with Custom Claims and Security Rules. Basically, you stand up a separate node server, install the Firebase AdminSDK:
npm install firebase-admin --save
Generate/Download a Private Key from the Service Accounts tab in the Firebase Console and put that on your node server. Then simply create a bare bones node app to assign Custom Claims against each UID (user) that you wish. Something like below worked for me:
var admin = require('firebase-admin');
var serviceAccount = require("./the-key-you-generated-and-downloaded.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://xxxxxxxxxxxxxxxxxxxx.firebaseio.com"
});
admin.auth().setCustomUserClaims("whatever-uid-you-want-to-assign-claim-to", {admin: true}).then(() => {
console.log("Custom Claim Added to UID. You can stop this app now.");
});
That's it. You can now verify if the custom claim is applied by logging out of your app (if you were previously logged in) and logging back in after you update your web app's .onAuthStateChanged method:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
firebase.auth().currentUser.getIdToken()
.then((idToken) => {
// Parse the ID token.
const payload = JSON.parse(window.atob(idToken.split('.')[1]));
// Confirm the user is an Admin.
if (!!payload['admin']) {
//showAdminUI();
console.log("we ARE an admin");
}
else {
console.log("we ARE NOT an admin");
}
})
.catch((error) => {
console.log(error);
});
}
else {
//USER IS NOT SIGNED IN
}
});
I am using google cloud functions to register push notifications through firebase. In my app, i have a notifications reference that changes for a current user whenever they get a new follower or like, etc. As of right now I am able to send the notification to the phone whenever that whole reference child changes
For example, if any single post is liked, then it will send a notification. What I need to do is observe the current user to only send the notification that single person.
Here is my JavaScript file
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendPushNotification = functions.database.ref('/notification/{id}').onWrite(event => {
const payload = {
notification: {
title: 'New message arrived',
body: 'come check it',
badge: '1',
sound: 'default',
}
};
return admin.database().ref('fcmToken').once('value').then(allToken => {
if (allToken.val()) {
const token = Object.keys(allToken.val());
return admin.messaging().sendToDevice(token, payload).then(response => {
});
}
});
});
I would like to replace this line:
functions.database.ref('/notification/{id}').onWrite(event => {
With this:
functions.database.ref('/notification/{id}').(The current user ID).onWrite(event => {
How do I get the current users id?
You seem very new to JavaScript (calling it JSON is sort-of a give-away for that). Cloud Functions for Firebase is not the best way to learn JavaScript. I recommend first reading the Firebase documentation for Web developers and/or taking the Firebase codelab for Web developer. They cover many basic JavaScript, Web and Firebase interactions. After those you'll be much better equipped to write code for Cloud Functions too.
Now back to your question: there is no concept of a "current user" in Cloud Functions. Your JavaScript code runs on a server, and all users can trigger the code by writing to the database.
You can figure out what user triggered the function, but that too isn't what you want here. The user who triggered the notification is not the one who needs to receive the message. What you want instead is to read the user who is the target of the notification.
One way to do this is to read it from the database path that triggered the function. If you keep the notifications per user in the database like this:
user_notifications
$uid
notification1: ...
notification2: ...
You can trigger the Cloud Function like this:
exports.sendPushNotification = functions.database.ref('/user_notification/{uid}/{id}').onWrite(event => {
And then in the code of that function, get the UID of the user with:
var uid = event.params.uid;
For Swift 3.0 - 4.0
You can do this:
import Firebase
import FirebaseAuth
class YourClass {
let user = Auth.auth().currentUser
let userID = user.uid
// user userID anywhere
}