Spaces:
Running
Running
| import { OAuth2Client } from 'google-auth-library' | |
| const FIREBASE_PROJECT_ID = process.env.FIREBASE_PROJECT_ID || '' | |
| // Cache certs β Firebase rotates them ~every 24 h | |
| let _certsCache = null | |
| let _certsFetchedAt = 0 | |
| async function getFirebaseCerts() { | |
| const now = Date.now() | |
| if (!_certsCache || now - _certsFetchedAt > 6 * 60 * 60 * 1000) { | |
| const res = await fetch( | |
| 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com' | |
| ) | |
| _certsCache = await res.json() | |
| _certsFetchedAt = now | |
| } | |
| return _certsCache | |
| } | |
| async function verifyFirebaseToken(token) { | |
| const certs = await getFirebaseCerts() | |
| const client = new OAuth2Client() | |
| const ticket = await client.verifySignedJwtWithCertsAsync( | |
| token, | |
| certs, | |
| FIREBASE_PROJECT_ID, | |
| ['https://securetoken.google.com/' + FIREBASE_PROJECT_ID], | |
| ) | |
| const p = ticket.getPayload() | |
| return { uid: p.sub, email: p.email, name: p.name } | |
| } | |
| export async function requireAuth(req, res, next) { | |
| if (!FIREBASE_PROJECT_ID) { | |
| if (process.env.NODE_ENV !== 'production') return next() | |
| return res.status(500).json({ error: 'Auth not configured on server.' }) | |
| } | |
| const authHeader = req.headers.authorization || '' | |
| const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : null | |
| if (!token) return res.status(401).json({ error: 'Unauthorized β missing token.' }) | |
| try { | |
| req.user = await verifyFirebaseToken(token) | |
| next() | |
| } catch (err) { | |
| console.error('[Auth]', err.message) | |
| res.status(401).json({ error: 'Unauthorized β invalid or expired token.' }) | |
| } | |
| } | |