90 lines
2.5 KiB
TypeScript
90 lines
2.5 KiB
TypeScript
|
import { Context, Handler } from "hono";
|
||
|
import {
|
||
|
EmulatorEnv,
|
||
|
Auth,
|
||
|
WorkersKVStoreSingle,
|
||
|
KeyStorer,
|
||
|
FirebaseIdToken,
|
||
|
} from "firebase-auth-cloudflare-workers";
|
||
|
|
||
|
export interface VerifyFirebaseAuthEnv extends EmulatorEnv {
|
||
|
PUBLIC_JWK_CACHE_KEY?: string | undefined;
|
||
|
PUBLIC_JWK_CACHE_KV?: KVNamespace | undefined;
|
||
|
}
|
||
|
|
||
|
export interface VerifyFirebaseAuthConfig {
|
||
|
projectId: string;
|
||
|
authorizationHeaderKey?: string;
|
||
|
keyStore?: KeyStorer;
|
||
|
keyStoreInitializer?: (c: Context) => KeyStorer;
|
||
|
disableErrorLog?: boolean;
|
||
|
firebaseEmulatorHost?: string;
|
||
|
}
|
||
|
|
||
|
const defaultKVStoreJWKCacheKey = "verify-firebase-auth-cached-public-key";
|
||
|
const defaultKeyStoreInitializer = (c: Context): KeyStorer => {
|
||
|
return WorkersKVStoreSingle.getOrInitialize(
|
||
|
c.env.PUBLIC_JWK_CACHE_KEY ?? defaultKVStoreJWKCacheKey,
|
||
|
c.env.PUBLIC_JWK_CACHE_KV
|
||
|
);
|
||
|
};
|
||
|
|
||
|
export const verifyFirebaseAuth = (
|
||
|
userConfig: VerifyFirebaseAuthConfig
|
||
|
): Handler => {
|
||
|
const config = {
|
||
|
projectId: userConfig.projectId,
|
||
|
AuthorizationHeaderKey:
|
||
|
userConfig.authorizationHeaderKey ?? "Authorization",
|
||
|
KeyStore: userConfig.keyStore,
|
||
|
keyStoreInitializer:
|
||
|
userConfig.keyStoreInitializer ?? defaultKeyStoreInitializer,
|
||
|
disableErrorLog: userConfig.disableErrorLog,
|
||
|
firebaseEmulatorHost: userConfig.firebaseEmulatorHost,
|
||
|
};
|
||
|
|
||
|
return async (c, next) => {
|
||
|
const authorization = c.req.headers.get(config.AuthorizationHeaderKey);
|
||
|
if (authorization === null) {
|
||
|
return new Response(null, {
|
||
|
status: 400,
|
||
|
});
|
||
|
}
|
||
|
const jwt = authorization.replace(/Bearer\s+/i, "");
|
||
|
const auth = Auth.getOrInitialize(
|
||
|
config.projectId,
|
||
|
config.KeyStore ?? config.keyStoreInitializer(c)
|
||
|
);
|
||
|
|
||
|
try {
|
||
|
const idToken = await auth.verifyIdToken(jwt, {
|
||
|
FIREBASE_AUTH_EMULATOR_HOST:
|
||
|
config.firebaseEmulatorHost ?? c.env.FIREBASE_AUTH_EMULATOR_HOST,
|
||
|
});
|
||
|
setFirebaseToken(c, idToken);
|
||
|
} catch (err) {
|
||
|
if (!userConfig.disableErrorLog) {
|
||
|
console.error({
|
||
|
message: "failed to verify the requested firebase token",
|
||
|
err,
|
||
|
});
|
||
|
}
|
||
|
return new Response(null, {
|
||
|
status: 401,
|
||
|
});
|
||
|
}
|
||
|
await next();
|
||
|
};
|
||
|
};
|
||
|
|
||
|
const idTokenContextKey = "firebase-auth-cloudflare-id-token-key";
|
||
|
|
||
|
const setFirebaseToken = (c: Context, idToken: FirebaseIdToken) =>
|
||
|
c.set(idTokenContextKey, idToken);
|
||
|
|
||
|
export const getFirebaseToken = (c: Context): FirebaseIdToken | null => {
|
||
|
const idToken = c.get(idTokenContextKey);
|
||
|
if (!idToken) return null;
|
||
|
return idToken;
|
||
|
};
|