Firebase console shows empty identifier for custom authentication - node.js

I use a custom auth scheme for Firebase. Tokens are generated by my nodejs backend:
mFirebase
.getAuth(token)
.createCustomToken(uid, claims)
.then(sendToken)
.
.
When I check out the "Users" section of Firebase console, the "Identifier" field is always empty.
I wonder if this is the expected behavior or I can provide additional things from my backend such as the username or email, to be displayed there.

This is expected. You need to set the email on the user if you want it to show.
You have 2 options.
set before you mint custom token:
return admin.auth().createUser({
uid: 'customUserUid',
email: 'user#example.com'
}).then((userRecord) => {
return admin.auth().createCustomToken(userRecord.uid);
}).catch((error) => {
// Error.
});
Update user email client side after sign in:
firebase.auth().signInWithCustomToken(customToken).then((result) => {
return result.user.updateEmail(userEmail);
}).catch((error) => {
// Error.
});

Related

Is there a way to tell if a user is registered with phone number or email & password on Firebase Auth

I am building a React App with Firebase back end.
I have two types of users, Admin and Standard users. The admins are created by a Super Admin using email and password and their phone numbers is set to their profile. The Standard user registers using phone number. I would like to send a different registration success SMS for the Admin and the standard user using Firebase cloud functions with the onCreate auth trigger.
Is there a way to tell which user was created through Admin SDK and which one registered through phone Auth.
My create Admin function is as below.
exports.addAdmin= functions.https.onCall((data, context) => {
return admin.auth().createUser({
email: data.email,
password: data.password,
displayName: data.name,
phoneNumber: data.phone,
disabled: false,
emailVerified: true
})
});
You can check if email and phoneNumber are defined in user object of onCreate() function like this:
export const onUserCreate = auth.user().onCreate(async (user) => {
if (user.phoneNumber) {
// User has phone number
}
if (user.email) {
// User has email
}
});
If both admin created and standard users can have email-password auth setup and you want check if they were created by a super admin or no, then you'll have to store the source in a database or custom claims. Try:
export const addAdmin = https.onCall(async (data, context) => {
const user = await getAuth().createUser({
email: "dharmaraj.r244#gmail.com",
password: "data.password",
});
// Add 'source' custom claim
await getAuth().setCustomUserClaims(user.uid, {
source: "admin",
});
return 'User created';
});
export const onUserCreate = auth.user().onCreate(async (user) => {
const { customClaims } = await getAuth().getUser(user.uid);
if (customClaims.source === "admin") {
console.log("User created by super admin")
}
return null;
});

How to set custom auth claims through Firebase and identify platform

I am following the firebase documentation here to set custom auth claims for users logging into my app for the first time using firebase auth + identify platform but it does not seem to be working.
When a user logs in for the first time, I want them to get the admin custom claim. I have created the following blocking function and have verified from the logs that it runs when I log in for the first time to my app using sign-in with google:
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
return {
customClaims: {
admin: true,
},
};
});
I would expect this to create the admin custom claim in the user's token. However, when I get a list of claims using another cloud function the admin claim does not appear.
exports.getclaims = functions.https.onRequest(async (req, res) => {
const uid = req.query.uid as string;
if (uid) {
const user = await admin.auth().getUser(uid);
res.send(user.customClaims);
} else {
res.sendStatus(500);
}
});
If I set the claim using the admin SDK directly using the below cloud function, the admin claim does appear.
exports.setclaim = functions.https.onRequest(async (req, res) => {
const uid = req.query.uid as string;
if (uid) {
await admin.auth().setCustomUserClaims(uid, {admin: true});
res.sendStatus(200);
} else {
res.sendStatus(500);
}
});
What am I doing wrong in the beforeCreate function?
There's an open GitHub issue regarding that. See sessionClaims content not getting added to the decoded token. Also, there's a fix that has been recently merged regarding this issue.
From the snippet you provided, there does not appear to be anything wrong with beforeCreate as coded.
You may want to check you do not have a beforeSignIn that is overwriting the customClaims directly or via sessionClaims.
https://firebase.google.com/docs/auth/extend-with-blocking-functions#modifying_a_user
Try to use onCreate method instead of beforeCreate how it is shown on the official docs
functions.auth.user().onCreate(async (user) => {
try {
// Set custom user claims on this newly created user.
await getAuth().setCustomUserClaims(user.uid, {admin: true});
// Update real-time database to notify client to force refresh.
const metadataRef = getDatabase().ref('metadata/' + user.uid);
// Set the refresh time to the current UTC timestamp.
// This will be captured on the client to force a token refresh.
await metadataRef.set({refreshTime: new Date().getTime()});
} catch (error) {
console.log(error);
}
}
});
The main point here is that you need to create the user at first and then update claims and make the force update of the token at the client side:
firebase.auth().currentUser.getIdToken(true);

Error with generating reset password email via Firebase SDK Node

Currently I am trying to implement a reset Link for an individual who forgets their password. I keep getting an error that states sendCustomPasswordResetEmail is not defined. I am fairly new to Firebase SDK so any suggestions would be greatly appreciated. Here is my function:
exports.resetPassword = (req, res) => {
const userEmail = req.body.email
admin
.auth()
.generatePasswordResetLink(userEmail)
.then(link => {
// Construct password reset email template, embed the link and send
// using custom SMTP server.
return sendCustomPasswordResetEmail(userEmail, link)
})
.catch(error => {
console.log(error)
})
}
Do you need to send a custom email? Otherwise you could act as client and send a normal password reset email as described in the docs for web.

Not getting email

I am using google sign in on my app and I am using ionic v3 and the native google plugin so the user is directed to google to sign in and allow us access. Once the user allows access and I send the request to my backend where i use the userId sent from Google to get the users info. I get all the info I need except for the email address. Not sure why the users email is not showing up. Here is my backend code:
const plus = googleAPI.google.plus({
version: 'v1',
auth: 'My API Key'
});
plus.people.get({
userId: googleUserId,
fields: 'displayName,emails,name'
})
.then((user) => {
console.log('Google plus user: ' + JSON.stringify(user.data));
console.log('Emails: ' + user.data.emails)
})
.catch((err) => {
console.log('Google plus error: ' + err);
})
Seems like its a permissions issue because on the app I set a iOS webClientId which is different than my API key. If so how do I go about fixing it?
Figured it out the api call was missing the accessToken as part of the get

Feathersjs administrator role (or feathers middleware with auth check)

I have a stupid question with feathersjs auth hooks (or whatever). This is my code with comment:
app.get('/admin', function (req, res) {
// i'd like to check here if (req.connection.isAdmin) then render page
res.render('admin/admin.html', {
title: 'admin'
})
});
I can't find where i can implement user-auth hook to chek user for admin role. How can i do that?
You should be able to use the example that I posted in this other question: Feathers Js Restrict Access To Page on Server Side
In your case, you'll need to do some logic to see if the user is an administrator. By default, the JWT token that Feathers gives you will only contain the userId. You can retrieve the user record to check if the user is an administrator.
Here's an example of what you would put inside the jwt.verify block:
jwt.verify(token, secret, function(err, decoded) {
if (err) {
return res.status(401).send('You are not authorized to view that page.');
}
app.service('users')
.get(decoded.id) // Or _id if you're using MongoDB
.then(user => {
if (user.isAdmin) {
res.render('admin/admin.html', {
title: 'admin'
})
} else {
return res.status(401).send('You are not authorized to view that page.');
}
})
});
It will be possible in the next version of feathers-authentication to add values to the JWT on the server, so, for administrators, you could add an isAdmin property to the JWT payload. This will be fairly trivial to do once the pre-release version is published. Until then, the above is probably the best way to go.

Resources