feat: Add trusted proxy email authentication for Cloud Run + IAP agent access#19
Open
adhishthite wants to merge 8 commits intoEveryInc:mainfrom
Open
feat: Add trusted proxy email authentication for Cloud Run + IAP agent access#19adhishthite wants to merge 8 commits intoEveryInc:mainfrom
adhishthite wants to merge 8 commits intoEveryInc:mainfrom
Conversation
Co-authored-by: adhishthite <31769894+adhishthite@users.noreply.github.com>
Co-authored-by: adhishthite <31769894+adhishthite@users.noreply.github.com>
Remove email_headers, allowed_email_domains, and allowed_emails from /.well-known/agent.json public discovery. Only advertise that trusted_proxy_email auth is available, not internal config details. Add test: D2: discovery endpoint redacts trusted proxy allowlists
Remove x-forwarded-email from the default trusted headers fallback. When PROOF_TRUSTED_IDENTITY_EMAIL_HEADERS is not set, only x-goog-authenticated-user-email is trusted by default. Add test: D2: trusted proxy auth does not trust x-forwarded-email
For trusted_proxy_email auth, force ownerId to the authenticated email. If body.ownerId differs from the authenticated principal, reject with 403 FORBIDDEN_OWNER_ID_MISMATCH. Add principalProvider field to DirectShareAuthorizationResult for auth source tracking. Update docs to reflect that ownerId is now enforced, not optional. Add test: D2: trusted proxy auth rejects ownerId mismatches
- Validate body.ownerId and query.ownerId independently (not coalesced) - Reject empty/whitespace-only ownerId strings for trusted_proxy_email auth - Add isRejectedTrustedProxyOwnerId helper for clean validation logic - 5 new tests: query-only mismatch, dual-source conflict, empty ownerId, whitespace ownerId, api_key regression (arbitrary ownerId still allowed)
…gents Add trusted proxy email auth for Cloud Run + IAP agent access
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Proof SDK did not have a deployment path for agent access when hosted behind Cloud Run + Identity-Aware Proxy (IAP). In that setup, callers must first satisfy IAP (e.g., via an
acme-corp.comidentity) before Proof's own share-token auth applies. This PR adds support for trusting a proxy-injected email header as hosted auth, enabling agents to create and manage documents without a separate OAuth session.Problem
Agents running behind IAP-protected Cloud Run services couldn't authenticate with Proof SDK. The only auth paths were OAuth (requires browser interaction) or API keys (no identity tracking). This blocked production deployments where IAP already validates the caller.
Solution
Architecture
sequenceDiagram participant Agent participant IAP as Cloud IAP participant Proof as Proof SDK Agent->>IAP: Request + Google identity IAP->>IAP: Validate identity (acme-corp.com domain) IAP->>Proof: Forward + inject x-goog-authenticated-user-email Proof->>Proof: PROOF_TRUST_PROXY_HEADERS=true? alt Proxy trust enabled Proof->>Proof: Extract email from trusted header Proof->>Proof: Validate against allowed domains/emails Proof->>Proof: Set ownerId = authenticated email Proof-->>Agent: 200 OK (document created) else Proxy trust disabled Proof-->>Agent: Fall through to OAuth/API key auth endAuth Flow Detail
flowchart TD A[Incoming Request] --> B{PROOF_TRUST_PROXY_HEADERS?} B -->|false| C[Skip proxy auth] B -->|true| D[Read configured email headers] D --> E{Header present?} E -->|no| C E -->|yes| F[Normalize email: strip IAP prefix, lowercase] F --> G{Email in allowed domains/emails?} G -->|no| C[Fall through to OAuth] G -->|yes| H[Trusted identity established] H --> I{ownerId in request?} I -->|no| J[Set ownerId = authenticated email] I -->|yes| K{ownerId matches authenticated email?} K -->|yes| J K -->|no| L[403 FORBIDDEN_OWNER_ID_MISMATCH] style L fill:#ff6b6b,color:#fff style J fill:#51cf66,color:#fff style H fill:#339af0,color:#fffConfiguration
Example Usage
Once the caller has been admitted by IAP:
Security Measures
This PR went through two rounds of security review. Key security properties:
What's protected
/.well-known/agent.jsonno longer exposes trusted headers, allowed emails, or allowed domains. Only advertises thattrusted_proxy_emailauth is available.x-forwarded-emailx-goog-authenticated-user-email. Generic spoofable headers are not trusted unless explicitly configured.ownerIdvalidated independently against the authenticated email. Mismatches return403 FORBIDDEN_OWNER_ID_MISMATCH. Empty/whitespace values are also rejected.body.ownerIdand?ownerId=query param are checked separately. Conflicting values where one matches and one doesn't are rejected.principalProviderfield tracks whether auth came fromnone,api_key,oauth, ortrusted_proxy_email, enabling per-source policy in downstream logic.What operators must ensure
Changes
Core
server/hosted-auth.ts— New module for trusted proxy identity resolution. Parses IAP-style email headers, normalizes emails (stripsaccounts.google.com:prefix, lowercases), validates against allowed domains/emails.server/routes.ts—authorizeDirectShareRequestnow tracksprincipalProvideracross all auth paths.handleShareMarkdownenforces ownerId = authenticated email for trusted proxy auth with independent body/query validation.server/discovery-routes.ts—/.well-known/agent.jsonadvertisestrusted_proxy_emailinauth.methodswithout exposing internal config.Docs
README.md— Cloud Run + IAP setup section with configuration exampledocs/agent-docs.md— Agent-side flow documentation.env.example— New env vars documentedTests (8 new, 83 total)
x-forwarded-emailnot trusted by defaultRate Limiting
Testing
Review History