0

I am trying to make Firebase authentication work on the server.

'use strict';

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser')();
const cors = require('cors')({origin: true});
//const expressSanitizer = require('express-sanitizer');
const app = express();

// Express middleware that validates Firebase ID Tokens passed in the Authorization HTTP header.
// The Firebase ID token needs to be passed as a Bearer token in the Authorization HTTP header like this:
// `Authorization: Bearer <Firebase ID Token>`.
// when decoded successfully, the ID Token content will be added as `req.user`.

const validateFirebaseIdToken = (req, res, next) => {
   console.log('Check if request is authorized with Firebase ID token');

   if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&
  !(req.cookies && req.cookies.__session)) {
       console.error('No Firebase ID token was passed as a Bearer token in the Authorization header.',
    'Make sure you authorize your request by providing the following HTTP header:',
    'Authorization: Bearer <Firebase ID Token>',
    'or by passing a "__session" cookie.');
    res.redirect("/login");
    return;
   }

   let idToken;
   if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
   console.log('Found "Authorization" header');
     // Read the ID Token from the Authorization header.
     idToken = req.headers.authorization.split('Bearer ')[1];
   } else if(req.cookies) {
     console.log('Found "__session" cookie');
     // Read the ID Token from cookie.
     idToken = req.cookies.__session;
   } else {
     // No cookie
     res.redirect("/login");
     return;
   }

   admin.auth().verifyIdToken(idToken).then((decodedIdToken) => {
      console.log('ID Token correctly decoded', decodedIdToken);
      req.user = decodedIdToken;
      return next();
   }).catch((error) => {
      console.error('Error while verifying Firebase ID token:', error);
      res.redirect("/login");
   });
 };

app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static("/public"));
app.use(cors);
app.use(cookieParser);
//app.use(expressSanitizer());
//app.use(validateFirebaseIdToken);=
app.set("view engine", "ejs");

// This HTTPS endpoint can only be accessed by your Firebase Users.
// Requests need to be authorized by providing an `Authorization` HTTP header
// with value `Bearer <Firebase ID Token>`.
exports.app = functions.https.onRequest(app);



app.post("/login", (request, response) => {

   var idToken = request.body.token;

   console.log("REQUEST BODY = " + idToken);

   response.header("Authorization" , "Bearer " + idToken);

   return response.redirect("dashboard");
});

app.get("/dashboard", validateFirebaseIdToken, (request, response) => {
   response.redirect("/dashboard/new");
});

In the /login POST route, I am receiving the idToken as expected (and showed in the logs). It seems though, that the response is unable to preserve/maintain the header property Authentication: Bearer <Firebase ID token> set beforehand.

In fact, I sent a GET request in Postman to /dashboard by getting the idToken printed by the logs and setting it in the header of the request like Authorization: Bearer <idToken> and it worked perfectly.

Here it says that redirects are in fact new HTTPS requests and therefore don't preserve the header set in the response. What should I do in this case?

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
rgoncalv
  • 5,825
  • 6
  • 34
  • 61

1 Answers1

0

You have to send the Authorization header with every request. HTTPS functions are stateless. They don't remember anything from a prior request. So, you shouldn't depend on redirect behavior to retain state. Instead, the client needs to figure out where to go next and make the next request itself.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • Could you please elaborate? I didn't understand much. Isn't express going to receive the idToken and verify it using the admin SDK? What should I do? – rgoncalv Jun 17 '18 at 22:35
  • I get the point that I have to send the idToken from the client (which would be the best way to do the request from inside the `AuthStateChanged` callback?), but how do I ensure the Authorization header is in every request? – rgoncalv Jun 17 '18 at 22:44
  • In my answer, I said that you should NOT depend on a redirect. Find another way to get your work done without using a redirect, as it won't let you specify the header in the redirected request. – Doug Stevenson Jun 17 '18 at 23:27
  • Any suggestions you would recommend? – rgoncalv Jun 17 '18 at 23:30