diff --git a/CHANGELOG.md b/CHANGELOG.md index 31025713e..5ba90892d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,2 @@ - Fixes an emulator issue where snapshot.ref could not point to multiple databases (#1339). +- Fixes emulated v1 callable functions that use a monorepo setup (#1345). diff --git a/src/common/providers/https.ts b/src/common/providers/https.ts index cfa0aadec..3f59bfe39 100644 --- a/src/common/providers/https.ts +++ b/src/common/providers/https.ts @@ -36,6 +36,9 @@ import { TaskContext } from "./tasks"; const JWT_REGEX = /^[a-zA-Z0-9\-_=]+?\.[a-zA-Z0-9\-_=]+?\.([a-zA-Z0-9\-_=]+)?$/; +const CALLABLE_AUTH_HEADER = "x-callable-context-auth"; +const ORIGINAL_AUTH_HEADER = "x-original-auth"; + /** An express request with the wire format representation of the request body. */ export interface Request extends express.Request { /** The wire format representation of the request body. */ @@ -661,6 +664,32 @@ function wrapOnCallHandler( } const context: CallableContext = { rawRequest: req }; + + // TODO(colerogers): yank this when we release a breaking change of the CLI that removes + // our monkey-patching code referenced below and increases the minimum supported SDK version. + // + // Note: This code is needed to fix v1 callable functions in the emulator with a monorepo setup. + // The original monkey-patched code lived in the functionsEmulatorRuntime + // (link: https://github.com/firebase/firebase-tools/blob/accea7abda3cc9fa6bb91368e4895faf95281c60/src/emulator/functionsEmulatorRuntime.ts#L480) + // and was not compatible with how monorepos separate out packages (see https://github.com/firebase/firebase-tools/issues/5210). + if (isDebugFeatureEnabled("skipTokenVerification") && handler.length === 2) { + const authContext = context.rawRequest.header(CALLABLE_AUTH_HEADER); + if (authContext) { + logger.debug("Callable functions auth override", { + key: CALLABLE_AUTH_HEADER, + value: authContext, + }); + context.auth = JSON.parse(decodeURIComponent(authContext)); + delete context.rawRequest.headers[CALLABLE_AUTH_HEADER]; + } + + const originalAuth = context.rawRequest.header(ORIGINAL_AUTH_HEADER); + if (originalAuth) { + context.rawRequest.headers["authorization"] = originalAuth; + delete context.rawRequest.headers[ORIGINAL_AUTH_HEADER]; + } + } + const tokenStatus = await checkTokens(req, context); if (tokenStatus.auth === "INVALID") { throw new HttpsError("unauthenticated", "Unauthenticated");