Authorization Header is undefined

I am using Netlify Functions to set up a GraphQL layer between my app and the Firebase API I’m currently working with. I am getting an ID Token from Google in my client and attempting to send it to the lambda so that it can be passed to Firebase’s authentication service. I’m fairly new to authorization in web apps, but worked through guides from both Firebase and Google to get things implemented, and settled on using the Authorization header to pass the ID Token to the Function as it sounded like the most secure option.

I’m running into a strange issue though, where the "authorization" key on the event.headers object is defined in some places but not in others.

I have included a copy of my lambda’s code below.

const firebase = require("firebase");

exports.handler = function(event, context, callback) {
  if (!firebase.apps.length) {
    firebase.initializeApp(/* Firebase options */);
  }

  var credential = firebase.auth.GoogleAuthProvider.credential(event.headers.authorization);
  
  firebase
    .auth()
    .signInWithCredential(credential)
    .then(() => {
      firebase
        .firestore()
        .collection("books")
        .where("userId", "==", firebase.auth().currentUser.uid)
        .get()
        .then(data => {
          callback(null, {
            body: JSON.stringify(data.docs.map(doc => doc.data())),
            headers: {
              "Access-Control-Allow-Headers": "*",
              "Access-Control-Allow-Origin": "*"
            },
            statusCode: 200
          });
        });
    });
};

When the handler attempts to access event.headers.authorization on line 5 of the handler, Firebase throws an error due to the idToken being undefined.

The odd thing here, from my perspective, is that if I modify the handler to simply echo the header back to me on the client, it is defined and works fine. An example of code where the header is defined is included below.

exports.handler = function(event, context, callback) {
  callback(null, {
    body: event.headers.authorization,
    headers: {
      "Access-Control-Allow-Headers": "*",
      "Access-Control-Allow-Origin": "*"
    },
    statusCode: 200
  });
};

I should also add that if I pass the ID Token as a queryStringParameter, I see the value fine and can correctly authenticate with Firebase and fetch my data. I’m trying to avoid this however as I understand it can be a security hole when the url is cached elsewhere or displayed in logs.

Finally, these errors are occurring while I am testing the function locally, using netlify-lambda serve, and below is a copy of the request that is being made from the client side.

fetch(`${process.env.REACT_APP_API_BASE_URL}/index`, {
  method: "GET",
  headers: {
    Authorization: `Bearer ${user.getAuthResponse().id_token}`
  }
})

Can someone help me understand what I’m missing here, and why the event.headers.authorization value is undefined in the first code sample?

Thanks in advance! :slight_smile:

1 Like

Hi, just to be clear, are you having the same issue with netlify lambda in production?

Hi, and thank you for the response.

I have not attempted to push the broken version to production yet, since it was not working. Is there a subset of the functionality that will only work when published, and not when using the official development tools?

there is not perfect feature parity between local/remote particularly around complicated things like Auth.

I am not trying to suggest that you potentially break production, though! It would however be useful to try a draft deploy (if you use the CLI to publish) or a non-production branch deploy (if you use git-backed deploys) to test on our CDN versus on your local to try to be sure of where the bug is.

Thanks in advance for your help in troubleshooting!

Hi @anon6271552 thanks for reaching out and sharing your solution with the community!

It’s happened to me

The problem was that the function and the client didn’t run on the same origin (different ports, irrelevant story). In CORS case, the client first sends an OPTIONS and wait for the server (the function in this case) to return if the original request is allowed

The solution (that I ended up), if it’s not possible to run the client and the function on the same origin, is to respond to the OPTIONS request with status 200 and the appropriate headers

export const handler: Handler = async (request) => {
  if (request.httpMethod === 'OPTIONS') {
    return {
      headers: {
        'Allow': 'OPTIONS, POST', // or whatever http method I use
        'Access-Control-Allow-Headers': 'Content-Type, Authorization',
      },
      statusCode: 200,
    }
  }
}