I am following this documentation: Manage Session Cookies
My app.js looks like this basically sign in the user on the client side on a button click.
(function() {
// Initialize Firebase
var config = {
//...
};
firebase.initializeApp(config);
// no local persistence because of the httpOnly flag
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE);
const emailField = document.getElementById("email");
const passwordField = document.getElementById("password");
const loginButton = document.getElementById("loginButton");
loginButton.addEventListener("click", e => {
const email = emailField.value;
const password = passwordField.value;
const signInPromise = firebase.auth().signInWithEmailAndPassword(email, password);
signInPromise.catch(e => {
console.log("Login Error: " + e.message);
})
return signInPromise.then(() => {
console.log("Signed in + " + firebase.auth().currentUser.uid);
return firebase.auth().currentUser.getIdToken().then(idToken => {
// Session login endpoint is queried and the session cookie is set.
// CSRF protection should be taken into account.
// ...
// const csrfToken = getCookie('csrfToken')
console.log("User ID Token: " + idToken);
return sendToken(idToken);
//return postIdTokenToSessionLogin('/sessionLogin', idToken, csrfToken);
});
})
});
firebase.auth().onAuthStateChanged(user => {
if (user) {
document.getElementById('loginSuccess').innerHTML = `Signed in as ${user.uid}`;
document.getElementById('loginError').innerHTML = "";
} else {
document.getElementById('loginSuccess').innerHTML = "";
document.getElementById('loginError').innerHTML = `Not signed in`;
}
});
})();
The sendToken function looks like this:
function sendToken(idToken) {
console.log("Posting " + idToken);
var xhr = new XMLHttpRequest();
var params = `token=${idToken}`;
xhr.open('POST', "/admin/login", true);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
return new Promise(function(resolve, reject) {
xhr.onreadystatechange = function() {//Call a function when the state changes.
if (xhr.readyState == 4 && xhr.status == 200) {
resolve();
} else if (xhr.readyState == 4 && xhr.status != 200) {
reject("Invalid http return status");
}
}
return xhr.send(params);
});
}
On the server side I use an express app with hosting and firebase functions
The /admin/login
post looks like this:
adminApp.post("/login", (request, response) => {
console.log("Got login post request");
if (request.body.token) {
const idToken = request.body.token.toString();
console.log("idToken = " + idToken);
// Set session expiration to 5 days.
const expiresIn = 60 * 60 * 24 * 5 * 1000;
return adminFirebase.auth().createSessionCookie(idToken, {expiresIn}).then((sessionCookie) => {
const options = {maxAge: expiresIn, httpOnly: true, secure: true};
response.cookie('session', sessionCookie, options);
response.end(JSON.stringify({status: 'success'}));
}, error => {
response.status(401).send('UNAUTHORIZED REQUEST!');
});
}
return response.status(400).send("MISSING TOKEN");
});
So after posting sendToken
I should have a cookie called 'session', which contains information. So now I wrote a little middleware to check that token:
const validateLogin = function (req, res, next) {
const sessionCookie = req.cookies.session || '';
console.log(JSON.stringify(req.headers));
console.log("Verifying " + sessionCookie);
return adminFirebase.auth().verifySessionCookie(sessionCookie, true).then((decodedClaims) => {
console.log("decoded claims: " + decodedClaims);
next();
}).catch(error => {
res.redirect('/admin/login');
});
};
Last but not least, I have a admin/secret
get that is using this middleware:
adminApp.get("/secret/", validateLogin, (request, response) => {
return response.send("This is secret!");
});
I, however, am constantly sent back to the login page. What am I missing for cookies to work?
I found out Firebase Hosting according to this only allows one cookie (otherwise they are stripped). This cookies is __session, however setting this cookies also does not seem to work for me...
I was able to set the __session cookie at the client side:
document.cookie = "__session=TOKEN"
and then verify the token on the server side, however the cookie only works for the local /
path and not /a/b
Firebase Auth provides server-side session cookie management for traditional websites that rely on session cookies.
The Firebase Admin SDK has a built-in method for creating custom tokens. At a minimum, you need to provide a uid , which can be any string but should uniquely identify the user or device you are authenticating. These tokens expire after one hour.
Every time a user signs in, the user credentials are sent to the Firebase Authentication backend and exchanged for a Firebase ID token (a JWT) and refresh token. Firebase ID tokens are short lived and last for an hour; the refresh token can be used to retrieve new ID tokens.
Session cookies are cookies that last for a session. A session starts when you launch a website or web app and ends when you leave the website or close your browser window. Session cookies contain information that is stored in a temporary memory location which is deleted after the session ends.
If somebody else is getting to this page (like I did an hour ago) here is the code in frontend that deals with this problem:
// Sign in with email and pass.
firebase.auth().signInWithEmailAndPassword(email, password)
.then(user => {
// Get the user's ID token and save it in the session cookie.
return firebase.auth().currentUser.getIdToken(true).then(function (token) {
// set the __session cookie
document.cookie = '__session=' + token + ';max-age=3600';
})
})
.catch(function (error) {//... code for error catching
I hope it helps.
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