Skip to content

Cannot verify token id even though it hasn't expired #8823

@CMadden8

Description

@CMadden8

Operating System

Windows 11

Environment (if applicable)

NodeJS v22.13.0, Angular

Firebase SDK Version

11.9.0

Firebase SDK Product(s)

Auth

Project Tooling

I'm using Angular to send the auth token to a Node JS back end via an interceptor.

Detailed Problem Description

If I decode the token, all the details are correct - the user etc and it hasn't expired.

I'm using the firebase emulators:start command to run the BE locally.

Here's the relevant code:

import express from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';
import helmet from 'helmet';
import { routes } from './routes/routes';
import dotenv from 'dotenv';
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import { getAuth } from 'firebase-admin/auth';
const { initializeApp } = require('firebase-admin/app');

dotenv.config();

initializeApp();

const app = express();

// Define allowed origins
const allowedOrigins = [
  'http://localhost:4200',
  'https://swiftee-e2c5f.web.app',
];

// CORS middleware
app.use(
  cors({
    origin: (origin, callback) => {
      if (!origin) {
        // Allow requests from mobile apps or curl (no origin)
        callback(null, true);
        return;
      }

      const originMatches = allowedOrigins.some(
        (allowedOrigin) => allowedOrigin === origin,
      );
      if (originMatches) {
        callback(null, true); // Allow the request
      } else {
        callback(new Error('Not allowed by CORS'));
      }
    },
    methods: 'GET,POST,PUT,DELETE,OPTIONS',
    allowedHeaders: 'Content-Type,Authorization',
  }),
);

app.use(helmet()); // Helmet helps secure your app by setting various HTTP headers
app.use(bodyParser.json());

// Authentication middleware
const authenticate = async (req, res, next) => {
  if (
    !req.headers.authorization ||
    !req.headers.authorization.startsWith('Bearer ')
  ) {
    console.error('❌ No Authorization header found');
    return res.status(403).send('Unauthorized');
  }

  const token = req.headers.authorization.split('Bearer ')[1];

  console.log(token, 'token');

  getAuth()
    .verifyIdToken(token)
    .then((decodedToken) => {
      const uid = decodedToken.uid;
      console.log(decodedToken, 'decoded token');
    })
    .catch((error) => {
      // Handle error
      console.log(error, 'gone wrong');
    });

  // try {
  //   const decodedToken = await getAuth().verifyIdToken(token);
  //   console.log('✅ Decoded Token:', decodedToken);

  //   req.user = decodedToken; // 🔹 Store user info in request object

  //   next(); // Proceed to the next middleware
  // } catch (error) {
  //   console.error('❌ Authentication error sdfsfs:', error);
  //   return res.status(403).send('Unauthorized');
  // }
};

// Protect all routes with authentication middleware
app.use(authenticate);

From the logs:

FirebaseAuthError: Firebase ID token has invalid signature. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.
22:15:11
function[us-central1-api]
    at FirebaseTokenVerifier.mapJwtErrorToAuthError (C:\Users\madde\Desktop\github\AI-App\org\node_modules\firebase-admin\lib\auth\token-verifier.js:275:20)
22:15:11
function[us-central1-api]
    at C:\Users\madde\Desktop\github\AI-App\org\node_modules\firebase-admin\lib\auth\token-verifier.js:255:24
22:15:11
function[us-central1-api]
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5) {
22:15:11
function[us-central1-api]
  errorInfo: {
22:15:11
function[us-central1-api]
    code: 'auth/argument-error',
22:15:11
function[us-central1-api]
    message: 'Firebase ID token has invalid signature. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.'
22:15:11
function[us-central1-api]
  },
22:15:11
function[us-central1-api]
  codePrefix: 'auth'
22:15:11
function[us-central1-api]
} gone wrong
22:16:11
function[us-central1-api]
Your function timed out after ~60s. To configure this timeout, see
      https://firebase.google.com/docs/functions/manage-functions#set_timeout_and_memory_allocation.
22:16:11
function[us-central1-api]
Request to function failed: Error: socket hang up

I get the feeling I must be doing something simple incorrectly. But what is it?

Steps and code to reproduce issue

Create a basic firebase app with Angular, leveraging @angular/fire.

Create some sign in methods (I'm using google sign in via Firebase for this).

Create a basic Angular interceptor:

import { inject } from '@angular/core';
import { HttpInterceptorFn } from '@angular/common/http';
import { Auth } from '@angular/fire/auth';
import { User } from 'firebase/auth';
import { from, switchMap } from 'rxjs';

export const authInterceptor: HttpInterceptorFn = (req, next) => {
  const auth = inject(Auth);

  // Get current user synchronously
  const user: User | null = auth.currentUser;

  // If the user is logged in, proceed to fetch the token
  if (user) {
    return from(user.getIdToken()).pipe(
      switchMap((token) => {
        console.log(token, 'token');

        const clonedReq = req.clone({
          setHeaders: { Authorization: `Bearer ${token}` },
        });
        return next(clonedReq); // Proceed with the cloned request
      }),
    );
  }

  // If no user is logged in, just proceed with the original request
  return next(req);
};

Create a Node JS back end to validate the token:

import express from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';
import helmet from 'helmet';
import { routes } from './routes/routes';
import dotenv from 'dotenv';
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import { getAuth } from 'firebase-admin/auth';
const { initializeApp } = require('firebase-admin/app');

dotenv.config();

initializeApp();

const app = express();

// Define allowed origins
const allowedOrigins = [
  'http://localhost:4200',
  'https://swiftee-e2c5f.web.app',
];

// CORS middleware
app.use(
  cors({
    origin: (origin, callback) => {
      if (!origin) {
        // Allow requests from mobile apps or curl (no origin)
        callback(null, true);
        return;
      }

      const originMatches = allowedOrigins.some(
        (allowedOrigin) => allowedOrigin === origin,
      );
      if (originMatches) {
        callback(null, true); // Allow the request
      } else {
        callback(new Error('Not allowed by CORS'));
      }
    },
    methods: 'GET,POST,PUT,DELETE,OPTIONS',
    allowedHeaders: 'Content-Type,Authorization',
  }),
);

app.use(helmet()); // Helmet helps secure your app by setting various HTTP headers
app.use(bodyParser.json());

// Authentication middleware
const authenticate = async (req, res, next) => {
  if (
    !req.headers.authorization ||
    !req.headers.authorization.startsWith('Bearer ')
  ) {
    console.error('❌ No Authorization header found');
    return res.status(403).send('Unauthorized');
  }

  const token = req.headers.authorization.split('Bearer ')[1];

  console.log(token, 'token');

  getAuth()
    .verifyIdToken(token)
    .then((decodedToken) => {
      const uid = decodedToken.uid;
      console.log(decodedToken, 'decoded token');
    })
    .catch((error) => {
      // Handle error
      console.log(error, 'gone wrong');
    });

  // try {
  //   const decodedToken = await getAuth().verifyIdToken(token);
  //   console.log('✅ Decoded Token:', decodedToken);

  //   req.user = decodedToken; // 🔹 Store user info in request object

  //   next(); // Proceed to the next middleware
  // } catch (error) {
  //   console.error('❌ Authentication error sdfsfs:', error);
  //   return res.status(403).send('Unauthorized');
  // }
};

// Protect all routes with authentication middleware
app.use(authenticate);

Run the firebase emulators:start command to test locally.

It will always go to the error scenario, with:

FirebaseAuthError: Firebase ID token has invalid signature. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.

Even though the token that's received hasn't expired.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions