-
Notifications
You must be signed in to change notification settings - Fork 986
Description
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.