Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase Cookies not saving

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

like image 558
Janosch Hübner Avatar asked Aug 12 '18 08:08

Janosch Hübner


People also ask

Does firebase Auth use cookies?

Firebase Auth provides server-side session cookie management for traditional websites that rely on session cookies.

How long does a firebase token last?

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.

How do I refresh my firebase token?

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.

What is a cookie session?

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.


1 Answers

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.

like image 180
SharpBCD Avatar answered Sep 29 '22 04:09

SharpBCD