Skip to content

feat(identity): support IS_NOT_TENANT filtering for v2 Auth events - #319

Merged
shettyvarun268 merged 1 commit into
mainfrom
shettyvarun268/auth-v2-is-not-tenant
Oct 22, 2026
Merged

shettyvarun268 merged 1 commit into
mainfrom
shettyvarun268/auth-v2-is-not-tenant

Conversation

@shettyvarun268

@shettyvarun268 shettyvarun268 commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Note: This PR is stacked on #318.

Adds IS_NOT_TENANT support to AuthOptions.tenant_id so developers can scope on_user_created and on_user_deleted triggers exclusively to default-project (non-tenant) users, matching the Node.js SDK (firebase-functions/v2/identity).


Usage

from firebase_functions import identity_fn

# Only triggers for users in the default project (ignores users created in any tenant)
@identity_fn.on_user_created(tenant_id=identity_fn.IS_NOT_TENANT)
def handle_default_project_user_created(event: identity_fn.AuthEvent) -> None:
    assert event.tenant_id is None
    print(f"Default-project user created: {event.data.uid}")

Architectural & Implementation Details

Why IS_NOT_TENANT Uses Hybrid Deploy-Time + Runtime Filtering (options.py, identity_fn.py, private/_identity_fn.py)

In Identity Platform multi-tenancy, there are three ways a developer may configure tenant_id on an Auth trigger:

  1. tenant_id=None (default): Trigger for all users across the project and all tenants (eventFilters: {}).
  2. tenant_id="tenant-a": Trigger only for users in "tenant-a" (eventFilters: {"tenantid": "tenant-a"}).
  3. tenant_id=IS_NOT_TENANT: Trigger only for default-project users who do not belong to any tenant.

Platform Constraint: Google Cloud Eventarc does not support negative filters or empty-string filters (eventFilters: {"tenantid": ""} is rejected by the Eventarc API). To support IS_NOT_TENANT (matching the Node SDK's implementation):

  • Public Sentinel Type: Defined a public Sentinel(_util.Sentinel) subclass and IS_NOT_TENANT = Sentinel(...) in firebase_functions.options (and re-exported IS_NOT_TENANT in firebase_functions.identity_fn) so private module paths do not leak into public Sphinx docs or IDE autocomplete.
  • Deploy-Time (AuthOptions._endpoint): When self.tenant_id is IS_NOT_TENANT, we omit "tenantid" from event_filters in functions.yaml so Eventarc delivers all Auth events to the function (while rejecting any other Sentinel such as RESET_VALUE with a ValueError).
  • Fast-Path Runtime Filtering (user_event_handler): When options.tenant_id == _options.IS_NOT_TENANT and the CloudEvent's top-level tenantid attribute is present (raw._get_attributes().get("tenantid") is not None), the handler returns immediately before parsing the CloudEvent payload or running @core.init.

Verification

Scenario on_user_created(tenant_id=IS_NOT_TENANT) on_user_created(tenant_id="tenant-a") on_user_created() (tenant_id=None)
Default-project user (tenant_id=None) Fires Ignored Fires
Tenant A user (tenant_id="tenant-a") Ignored (filtered at runtime) Fires Fires
Tenant B user (tenant_id="tenant-b") Ignored (filtered at runtime) Ignored (filtered by Eventarc) Fires
  • Automated Checks:
    • pytest (all 147 tests passing, including test_on_user_created_is_not_tenant)
    • mypy, ruff check, ruff format --check, and ./docs/generate.sh
  • End-to-End Testing:
    • Verified locally against the Firebase Auth + Functions Emulators (firebase emulators:exec).
    • Verified in production on Identity Platform across all 6 combinations of on_user_created / on_user_deleted $\times$ (IS_NOT_TENANT, specific tenant_id, and tenant_id=None).

PR Stack:

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for filtering Auth event triggers to only trigger on users in the default project (no tenant) by setting tenant_id to IS_NOT_TENANT. Since Eventarc does not support empty-string filters, tenant-scoped events are filtered out at runtime. Additionally, it adds support for parsing RFC-2822 timestamps emitted by the Firebase Auth Emulator. Feedback on the changes suggests ensuring that all parsed or provided datetime objects in _parse_auth_timestamp are normalized to timezone-aware UTC datetimes to prevent returning naive datetimes or datetimes with non-UTC offsets.

Comment thread src/firebase_functions/private/_identity_fn.py Outdated
@shettyvarun268
shettyvarun268 force-pushed the shettyvarun268/auth-v2-is-not-tenant branch 2 times, most recently from 552a64a to caefc5b Compare October 13, 2026 20:42
@shettyvarun268
shettyvarun268 force-pushed the shettyvarun268/auth-v2-is-not-tenant branch 5 times, most recently from 425136d to 87cb6b6 Compare October 13, 2026 23:05
@shettyvarun268
shettyvarun268 marked this pull request as ready for review October 13, 2026 23:13
@shettyvarun268
shettyvarun268 added this pull request to stack #320 October 14, 2026 14:57
@shettyvarun268
shettyvarun268 force-pushed the shettyvarun268/auth-v2-is-not-tenant branch from 87cb6b6 to 44bbe64 Compare October 15, 2026 15:43
@shettyvarun268
shettyvarun268 force-pushed the shettyvarun268/auth-v2-is-not-tenant branch 4 times, most recently from 7aa3ce4 to 2a1224c Compare October 19, 2026 16:40
@shettyvarun268 shettyvarun268 changed the title feat(identity): support IS_NOT_TENANT filtering and emulator timestamps feat(identity): support IS_NOT_TENANT filtering for v2 Auth events Oct 19, 2026
@shettyvarun268
shettyvarun268 force-pushed the shettyvarun268/auth-v2-is-not-tenant branch from 2a1224c to cfec286 Compare October 19, 2026 17:06

@wandamora wandamora left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, ty!

Comment thread src/firebase_functions/private/_identity_fn.py Outdated
Comment thread src/firebase_functions/options.py Outdated
@shettyvarun268
shettyvarun268 force-pushed the shettyvarun268/auth-v2-is-not-tenant branch from cfec286 to 54ae4f4 Compare October 21, 2026 18:43
Base automatically changed from shettyvarun268/auth-v2-triggers to main October 22, 2026 18:19
@shettyvarun268
shettyvarun268 force-pushed the shettyvarun268/auth-v2-is-not-tenant branch from 54ae4f4 to 39ef3ab Compare October 22, 2026 18:20
@shettyvarun268
shettyvarun268 merged commit 65303cd into main Oct 22, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants