feat(files): password, email-OTP, and SSO auth for public file shares#5140
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryHigh Risk Overview API & data: Product & policy: Share modal uses an access ButtonGroup (password, allow-list, client-reserved token before save). Refactors: Reviewed by Cursor Bugbot for commit ec5de63. Bugbot is set up for automated code reviews on this repo. Configure here. |
Greptile SummaryThis PR adds Password, Email-OTP, and SSO access controls to public file shares, unifying the gate with the existing deploy-as-chat auth machinery by extracting a shared
Confidence Score: 5/5Safe to merge. The auth logic is cleanly shared, the migration is additive, and the file-name-leak issues from earlier review rounds have been addressed. Cookie generation and validation both use the raw encrypted password from PublicShareRow, so the password-slot invalidation mechanism works as intended. All three new auth modes have IP rate limits on their write paths. The remaining comments are minor validation gaps and a UX inconsistency in the email countdown. apps/sim/app/api/files/public/[token]/otp/route.ts — the PUT verify endpoint would benefit from an IP rate limit to match the POST request path. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant C as Client
participant P as /f/[token] page.tsx
participant M as GET /api/files/public/[token]
participant O as POST/PUT /otp
participant S as POST /sso
participant A as validateDeploymentAuth
C->>P: Visit share URL
P->>P: resolveShare(token)
P->>P: renderAuthGate(share)
alt "authType = public"
P-->>C: PublicFileView
else "authType = password"
P-->>C: PublicFileAuth gate
C->>M: POST password
M->>A: validateDeploymentAuth
A-->>M: authorized true
M-->>C: Set file_auth cookie
C->>P: router.refresh()
P-->>C: PublicFileView
else "authType = email"
P-->>C: PublicFileEmailAuth
C->>O: POST email request OTP
O-->>C: 200 message
C->>O: PUT email otp verify
O-->>C: Set file_auth cookie
C->>P: router.refresh()
P-->>C: PublicFileView
else "authType = sso"
P->>P: getSession + isEmailAllowed
alt not authorized
P-->>C: PublicFileSSOAuth
C->>S: POST email eligibility
S-->>C: eligible true
C->>C: redirect to SSO
C->>P: return from SSO
P-->>C: PublicFileView
else authorized
P-->>C: PublicFileView
end
end
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant C as Client
participant P as /f/[token] page.tsx
participant M as GET /api/files/public/[token]
participant O as POST/PUT /otp
participant S as POST /sso
participant A as validateDeploymentAuth
C->>P: Visit share URL
P->>P: resolveShare(token)
P->>P: renderAuthGate(share)
alt "authType = public"
P-->>C: PublicFileView
else "authType = password"
P-->>C: PublicFileAuth gate
C->>M: POST password
M->>A: validateDeploymentAuth
A-->>M: authorized true
M-->>C: Set file_auth cookie
C->>P: router.refresh()
P-->>C: PublicFileView
else "authType = email"
P-->>C: PublicFileEmailAuth
C->>O: POST email request OTP
O-->>C: 200 message
C->>O: PUT email otp verify
O-->>C: Set file_auth cookie
C->>P: router.refresh()
P-->>C: PublicFileView
else "authType = sso"
P->>P: getSession + isEmailAllowed
alt not authorized
P-->>C: PublicFileSSOAuth
C->>S: POST email eligibility
S-->>C: eligible true
C->>C: redirect to SSO
C->>P: return from SSO
P-->>C: PublicFileView
else authorized
P-->>C: PublicFileView
end
end
Reviews (4): Last reviewed commit: "fix(security): make isEmailAllowed case-..." | Re-trigger Greptile |
…on renumber 0243→0244)
|
Addressed the review feedback in 7578702:
Also rebased onto latest staging (resolved the @greptile review |
…ed SSO denial message
|
Round 2 of bot feedback addressed:
@greptile review |
|
Round 3 (email casing, root-caused):
Net: allow-list matching is now case-insensitive everywhere; OTP storage keys off the normalized email on both store and verify. @greptile review |
|
Re the two Bugbot comments still showing on the latest commit (SSO session email casing @
Added a direct unit test for |
…api-validation baseline 859)
…ts non-password shares
|
[Bugbot High — "Public cookie bypasses email gate"] Fixed — good catch. Two layers:
Added a route test (password POST on a public share → 400, no cookie) and updated the chat auth-token assertion. Also rebased onto latest staging (migration renumbered to |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 3 total unresolved issues (including 2 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2120a46. Configure here.
|
The 'Share upsert validation returns 500' comment is already resolved as of |

Summary
validateChatAuthinto a sharedvalidateDeploymentAuth(resource, …, cookiePrefix); widensetDeploymentAuthCookieand the OTP store to a'file'kind; reuseisEmailAllowed,encryptSecret, the global/ssoflow, and the mailer/verification tableauth_type,password(encrypted),allowed_emailstopublic_share(migrations 0243/0244, additive)file_auth_{shareId}cookie (auth_required_*401 sentinels); addPOST /api/files/public/[token](password),…/otp(request+verify),…/sso(eligibility)AuthSelectorprecedent (ButtonGroup+ conditional password / allowed-emails fields); SSO hidden unlessNEXT_PUBLIC_SSO_ENABLED; link is reserved on open (client token, persisted on save) and shown live for non-private modes; removed the post-save link toastopengraph-image+generateMetadatafor/f/[token](rich for public shares, generic for password/email/SSO so the filename never leaks pre-auth; alwaysnoindex)GeneratedPasswordInput(deploy modal + share modal) andPublicFileAuthShell(the three gates)Type of Change
Testing
bun run lint✓ ·check:api-validation:strict✓ ·check:migrations origin/staging✓ (backward-compatible) ·tsc✓Checklist