I implemented cookie sessions on my firebase project using this link.
I'm trying to get the idToken
in the client side to send it to server side.
Here's the code I'm using on client side:
$(function(){
$.ajax({
url: "/checkSession",
type: 'post',
success: function(data) {
if(data.status === "ok" && data.user){
const user = data.user;
const emailVerified = user.email_verified;
const uid = user.uid;
if (emailVerified){
firebase.database().ref().child("users").child(uid).child("username").once("value", snapshot => {
const username = snapshot.val();
firebase.auth().currentUser.getIdToken(true).then(idToken => {
// Doesn't work...
})
}).catch(err => {
console.log(err)
});
}
}
}
});
});
And here's the server that checks for the session:
app.post("/checkSession", (req, res, next) => {
const sessionCookie = req.cookies.session || '';
admin
.auth()
.verifySessionCookie(sessionCookie, true)
.then(user => {
res.json({
status: "ok",
user
})
})
.catch((error) => {
res.json({
status: "error",
})
});
})
Everything works except this function: firebase.auth().currentUser.getIdToken(true).then(idToken => {...})
and it's throwing Cannot read property 'getIdToken' of null
error.
UPDATE: Here's the sign-in method I'm using. I'm passing false to httpOnly
and secure
since I'm testing it locally.
app.post("/SignInUser", (req, res, next) => {
const idToken = req.body.idToken.toString();
const expiresIn = 60 * 60 * 24 * 10 * 1000; // 10 days
admin
.auth()
.verifyIdToken(idToken)
.then(function (decodedClaims) {
if (new Date().getTime() / 1000 - decodedClaims.auth_time < 5 * 60) {
return admin.auth().createSessionCookie(idToken, { expiresIn });
}
})
.then(function (sessionCookie) {
const options = { maxAge: expiresIn, httpOnly: false, secure: false};
res.cookie("session", sessionCookie, options);
return res.json({
status: "ok"
})
})
.catch(function (error) {
return res.json({
status: "error",
message: "Unauthorized request!"
})
});
})
UPDATE 2: I use the idToken to verify that the request coming from is actually from the user and not from an external source, here's an example code:
function getUserSnapshotOrVerifyUserId(username, idToken, cb) {
if (username == null || username.length == 0 || idToken == null || idToken.length == 0)
return cb({
status: "error",
errorMessage: "Missing params."
}, null);
admin.auth().verifyIdToken(idToken).then(decodedToken => {
let uid = decodedToken.uid;
admin.database().ref().child("users").orderByChild("username").equalTo(username).once('value', snapshot => {
if (!snapshot.exists())
return cb({
status: "error",
message: "invalid-profile"
});
snapshot.forEach(child => {
const id = child.val().id;
if (id !== uid)
return cb({
status: "error",
message: "Invalid ID"
});
admin.database().ref("users/" + id).once("value", snapshot => {
if (!snapshot.exists())
return cb({
status: "error",
errorMessage: "user not found."
});
return cb(null, id, snapshot);
});
});
});
}).catch(err => cb({
status: "error",
message: err
}));
}
app.post("/getUserProfile", (req, res, next) => {
const username = req.body.username || req.query.username;
const idToken = req.body.idToken;
getUserSnapshotOrVerifyUserId(username, idToken, (err, id, snapshot) => {
if (err) return res.json(err);
let userdata = {
username: snapshot.val().username,
name: snapshot.val().name,
}
res.json({
status: "ok",
userdata
})
})
})
Firebase auth is asynchronous and you need to wait for it to fully initialize before trying to reference currentUser
. Please refer to this answer for more information on how to do that.
To put it into simpler terms, you need to listen to onAuthStatusChanged()
and wait for it to return null
or a User
object before calling any function that references currentUser
. I'd give you specific code changes but the relevant section isn't in the question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With