I've set up a firebase passport strategy on a NestJS server which works fine, but I did not like the long load times it would incur on all requests that went through it. So I decided to cache decoded tokens until they are expired, and this this massively reduced load times for valid and unexpired tokens.
However, I am concerned that there might be security risks connected to this. Mostly because this seemed like such a simple addition to me, someone must have thought about it before. I assume the people who made the firebase sdk must have considered adding it as a feature, but why haven't they?
For reference, here's the code for my passport strategy:
@Injectable()
export class FirebaseAuthStrategy extends PassportStrategy(Strategy, 'firebase-auth') {
private defaultApp: any;
constructor(
private configService: ConfigService,
@Inject(CACHE_MANAGER) private cacheManager: Cache
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken()
});
const config = this.configService.get<string>('FIREBASE_CONFIG');
if (!config) {
throw new Error('FIREBASE_CONFIG not available. Please ensure the variable is supplied in the `.env` file.');
}
const firebase_params = JSON.parse(config);
this.defaultApp = firebase.initializeApp({
credential: firebase.credential.cert(firebase_params)
});
}
async validate(token: string) {
const cachedFirebaseUser = await this.cacheManager.get(token);
if (cachedFirebaseUser) return cachedFirebaseUser;
const firebaseUser: any = await this.defaultApp
.auth()
.verifyIdToken(token, true)
.catch((err) => {
console.log(err);
throw new UnauthorizedException(err.Message);
});
if (!firebaseUser) {
throw new UnauthorizedException();
}
/**
* input parameter for `Date` or `moment` constructor is in milliseconds for unix timestamps.
* input * 1000 will instantiate correct Date or moment value.
* See here for reference: https://stackoverflow.com/a/45370395/5472560
*/
const exp = moment(+firebaseUser['exp'] * 1000);
const now = moment.now();
const ttl = exp.diff(now, 'seconds');
await this.cacheManager.set(token, firebaseUser, { ttl });
return firebaseUser;
}
}