From c49485e49f4fbdb79d4e8f02d69a8146741dd339 Mon Sep 17 00:00:00 2001 From: Varun Shetty Date: Thu, 11 Jun 2026 23:17:24 +0000 Subject: [PATCH 1/2] feature/add auth event arc experiment --- src/deploy/functions/services/index.ts | 2 ++ src/deploy/functions/validate.spec.ts | 49 ++++++++++++++++++++++++++ src/deploy/functions/validate.ts | 7 ++++ src/experiments.ts | 6 ++++ src/functions/events/v2.ts | 8 ++++- 5 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/deploy/functions/services/index.ts b/src/deploy/functions/services/index.ts index c79971daa34..afbaeffb973 100644 --- a/src/deploy/functions/services/index.ts +++ b/src/deploy/functions/services/index.ts @@ -207,6 +207,8 @@ const EVENT_SERVICE_MAPPING: Record = { "google.firebase.dataconnect.connector.v1.mutationExecuted": dataconnectService, "google.firebase.ailogic.v1.beforeGenerate": aiLogicService, "google.firebase.ailogic.v1.afterGenerate": aiLogicService, + "google.firebase.auth.user.v2.created": noOpService, + "google.firebase.auth.user.v2.deleted": noOpService, }; export function serviceForEndpoint(endpoint: backend.Endpoint | build.Endpoint): Service { diff --git a/src/deploy/functions/validate.spec.ts b/src/deploy/functions/validate.spec.ts index 34a527a0b73..1dcb0817308 100644 --- a/src/deploy/functions/validate.spec.ts +++ b/src/deploy/functions/validate.spec.ts @@ -9,6 +9,7 @@ import * as secretManager from "../../gcp/secretManager"; import * as backend from "./backend"; import { BEFORE_CREATE_EVENT, BEFORE_SIGN_IN_EVENT } from "../../functions/events/v1"; import { resolveCpuAndConcurrency } from "./prepare"; +import * as experiments from "../../experiments"; describe("validate", () => { describe("functionsDirectoryExists", () => { @@ -494,6 +495,54 @@ describe("validate", () => { "The following functions have timeouts that exceed the maximum allowed for their trigger typ", ); }); + + it("disallows v2 Auth Eventarc triggers if autheventarc experiment is not enabled", () => { + const sandbox = sinon.createSandbox(); + const assertEnabledStub = sandbox.stub(experiments, "assertEnabled"); + assertEnabledStub + .withArgs("autheventarc", sinon.match.any) + .throws(new FirebaseError("the experiment autheventarc is not enabled")); + + const ep: backend.Endpoint = { + ...ENDPOINT_BASE, + platform: "gcfv2", + eventTrigger: { + eventType: "google.firebase.auth.user.v2.created", + eventFilters: {}, + retry: false, + }, + }; + + try { + expect(() => validate.endpointsAreValid(backend.of(ep))).to.throw( + /the experiment autheventarc is not enabled/ + ); + } finally { + sandbox.restore(); + } + }); + + it("allows v2 Auth Eventarc triggers if autheventarc experiment is enabled", () => { + const sandbox = sinon.createSandbox(); + const assertEnabledStub = sandbox.stub(experiments, "assertEnabled"); + assertEnabledStub.withArgs("autheventarc", sinon.match.any).returns(); + + const ep: backend.Endpoint = { + ...ENDPOINT_BASE, + platform: "gcfv2", + eventTrigger: { + eventType: "google.firebase.auth.user.v2.created", + eventFilters: {}, + retry: false, + }, + }; + + try { + expect(() => validate.endpointsAreValid(backend.of(ep))).to.not.throw(); + } finally { + sandbox.restore(); + } + }); }); describe("endpointsAreUnqiue", () => { diff --git a/src/deploy/functions/validate.ts b/src/deploy/functions/validate.ts index fe12d75ff8b..da4d4327244 100644 --- a/src/deploy/functions/validate.ts +++ b/src/deploy/functions/validate.ts @@ -10,6 +10,8 @@ import * as fsutils from "../../fsutils"; import * as backend from "./backend"; import * as utils from "../../utils"; import * as secrets from "../../functions/secrets"; +import * as experiments from "../../experiments"; +import { AUTH_EVENTS } from "../../functions/events/v2"; /** * GCF Gen 1 has a max timeout of 540s. @@ -89,6 +91,11 @@ export function endpointsAreValid(wantBackend: backend.Backend): void { validateTimeoutConfig(endpoints); for (const ep of endpoints) { validateScheduledTimeout(ep); + if (backend.isEventTriggered(ep)) { + if ((AUTH_EVENTS as readonly string[]).includes(ep.eventTrigger.eventType)) { + experiments.assertEnabled("autheventarc", "deploy Auth Eventarc triggers"); + } + } const service = serviceForEndpoint(ep); if (backend.isBlockingTriggered(ep)) { if (service.name === "noop") { diff --git a/src/experiments.ts b/src/experiments.ts index 5daa8e826b3..53301161bf9 100644 --- a/src/experiments.ts +++ b/src/experiments.ts @@ -215,6 +215,12 @@ export const ALL_EXPERIMENTS = experiments({ default: false, public: true, }, + autheventarc: { + shortDescription: "Enable experimental Eventarc-based Auth triggers", + fullDescription: "Enable support for v2 Eventarc-based Auth triggers (onUserCreated and onUserDeleted)", + default: false, + public: true, + }, }); export type ExperimentName = keyof typeof ALL_EXPERIMENTS; diff --git a/src/functions/events/v2.ts b/src/functions/events/v2.ts index 8dd1fe9d691..177e9a0d251 100644 --- a/src/functions/events/v2.ts +++ b/src/functions/events/v2.ts @@ -41,6 +41,11 @@ export const FIREALERTS_EVENT = "google.firebase.firebasealerts.alerts.v1.publis export const DATACONNECT_EVENT = "google.firebase.dataconnect.connector.v1.mutationExecuted"; +export const AUTH_EVENTS = [ + "google.firebase.auth.user.v2.created", + "google.firebase.auth.user.v2.deleted", +] as const; + export type Event = | typeof PUBSUB_PUBLISH_EVENT | (typeof STORAGE_EVENTS)[number] @@ -51,7 +56,8 @@ export type Event = | (typeof FIRESTORE_EVENTS)[number] | typeof FIREALERTS_EVENT | typeof DATACONNECT_EVENT - | (typeof AI_LOGIC_EVENTS)[number]; + | (typeof AI_LOGIC_EVENTS)[number] + | (typeof AUTH_EVENTS)[number]; // Why can't auth context be removed? This is map was added to correct a bug where a regex // allowed any non-auth type to be converted to any auth type, but we should follow up for why From d1c9663265ef06aa40c349122c286ce9c1cfa3f4 Mon Sep 17 00:00:00 2001 From: Varun Shetty Date: Thu, 11 Jun 2026 23:27:22 +0000 Subject: [PATCH 2/2] review comments --- src/deploy/functions/validate.spec.ts | 18 +++++------------- src/deploy/functions/validate.ts | 4 +++- src/experiments.ts | 3 ++- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/deploy/functions/validate.spec.ts b/src/deploy/functions/validate.spec.ts index 1dcb0817308..22ee7ed6ed5 100644 --- a/src/deploy/functions/validate.spec.ts +++ b/src/deploy/functions/validate.spec.ts @@ -497,12 +497,7 @@ describe("validate", () => { }); it("disallows v2 Auth Eventarc triggers if autheventarc experiment is not enabled", () => { - const sandbox = sinon.createSandbox(); - const assertEnabledStub = sandbox.stub(experiments, "assertEnabled"); - assertEnabledStub - .withArgs("autheventarc", sinon.match.any) - .throws(new FirebaseError("the experiment autheventarc is not enabled")); - + experiments.setEnabled("autheventarc", false); const ep: backend.Endpoint = { ...ENDPOINT_BASE, platform: "gcfv2", @@ -515,18 +510,15 @@ describe("validate", () => { try { expect(() => validate.endpointsAreValid(backend.of(ep))).to.throw( - /the experiment autheventarc is not enabled/ + /Cannot deploy Auth Eventarc triggers because the experiment.*autheventarc.*is not enabled/, ); } finally { - sandbox.restore(); + experiments.setEnabled("autheventarc", null); } }); it("allows v2 Auth Eventarc triggers if autheventarc experiment is enabled", () => { - const sandbox = sinon.createSandbox(); - const assertEnabledStub = sandbox.stub(experiments, "assertEnabled"); - assertEnabledStub.withArgs("autheventarc", sinon.match.any).returns(); - + experiments.setEnabled("autheventarc", true); const ep: backend.Endpoint = { ...ENDPOINT_BASE, platform: "gcfv2", @@ -540,7 +532,7 @@ describe("validate", () => { try { expect(() => validate.endpointsAreValid(backend.of(ep))).to.not.throw(); } finally { - sandbox.restore(); + experiments.setEnabled("autheventarc", null); } }); }); diff --git a/src/deploy/functions/validate.ts b/src/deploy/functions/validate.ts index da4d4327244..aa5d66a9f7c 100644 --- a/src/deploy/functions/validate.ts +++ b/src/deploy/functions/validate.ts @@ -13,6 +13,8 @@ import * as secrets from "../../functions/secrets"; import * as experiments from "../../experiments"; import { AUTH_EVENTS } from "../../functions/events/v2"; +const AUTH_EVENTS_SET = new Set(AUTH_EVENTS); + /** * GCF Gen 1 has a max timeout of 540s. */ @@ -92,7 +94,7 @@ export function endpointsAreValid(wantBackend: backend.Backend): void { for (const ep of endpoints) { validateScheduledTimeout(ep); if (backend.isEventTriggered(ep)) { - if ((AUTH_EVENTS as readonly string[]).includes(ep.eventTrigger.eventType)) { + if (AUTH_EVENTS_SET.has(ep.eventTrigger.eventType)) { experiments.assertEnabled("autheventarc", "deploy Auth Eventarc triggers"); } } diff --git a/src/experiments.ts b/src/experiments.ts index 53301161bf9..b05c02e61f3 100644 --- a/src/experiments.ts +++ b/src/experiments.ts @@ -217,7 +217,8 @@ export const ALL_EXPERIMENTS = experiments({ }, autheventarc: { shortDescription: "Enable experimental Eventarc-based Auth triggers", - fullDescription: "Enable support for v2 Eventarc-based Auth triggers (onUserCreated and onUserDeleted)", + fullDescription: + "Enable support for v2 Eventarc-based Auth triggers (onUserCreated and onUserDeleted)", default: false, public: true, },