Skip to content

fix(api): pass explicit permission:undefined to better-auth hasPermis…#2751

Merged
Marfuen merged 1 commit into
mainfrom
mariano/fix-evidence-visibility
May 5, 2026
Merged

fix(api): pass explicit permission:undefined to better-auth hasPermis…#2751
Marfuen merged 1 commit into
mainfrom
mariano/fix-evidence-visibility

Conversation

@Marfuen
Copy link
Copy Markdown
Contributor

@Marfuen Marfuen commented May 5, 2026

…sion

Every cookie-authenticated request was 403'ing with "Unable to verify permissions" because better-auth 1.4.x's hasPermission body schema is a discriminated union that requires both keys present, with the unused side set to an explicit undefined. zod 4 (which better-auth's plugin uses internally) rejects "key is absent" — only "key is undefined" passes the z.undefined() branch:

z.union([
z.object({ permission: z.record(...), permissions: z.undefined() }),
z.object({ permission: z.undefined(), permissions: z.record(...) }),
])

Without permission: undefined in the body, the schema rejects every request with "[body] Invalid input"; the catch in canActivate turns that into a generic 403 and customers can't load the tasks page, findings, or any other endpoint protected by PermissionGuard.

API key requests aren't affected because they go through the scope-checking path earlier in the guard, which is why this only showed up for cookie sessions on customer accounts.

Test pins the body shape so a future refactor that drops the field gets caught.

What does this PR do?

  • Fixes #XXXX (GitHub issue number)
  • Fixes COMP-XXXX (Linear issue number - should be visible at the bottom of the GitHub issue description)

Visual Demo (For contributors especially)

A visual demonstration is strongly recommended, for both the original and new change (video / image - any one).

Video Demo (if applicable):

  • Show screen recordings of the issue or feature.
  • Demonstrate how to reproduce the issue, the behavior before and after the change.

Image Demo (if applicable):

  • Add side-by-side screenshots of the original and updated change.
  • Highlight any significant change(s).

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  • Are there environment variables that should be set?
  • What are the minimal test data to have?
  • What is expected (happy path) to have (input and output)?
  • Any other important info that could help to test that PR

Checklist

  • I haven't read the contributing guide
  • My code doesn't follow the style guidelines of this project
  • I haven't commented my code, particularly in hard-to-understand areas
  • I haven't checked if my changes generate no new warnings

Summary by cubic

Fix 403s for cookie-authenticated requests by sending permission: undefined alongside permissions to better-auth hasPermission. This satisfies the zod union schema and restores all PermissionGuard-protected endpoints; API key flows were unaffected.

  • Bug Fixes
    • Pass permission: undefined in the hasPermission body to match better-auth 1.4.x schema and avoid “[body] Invalid input” 403s.
    • Add a test that pins the body shape to prevent regressions.

Written for commit 832afdb. Summary will update on new commits.

…sion

Every cookie-authenticated request was 403'ing with "Unable to verify
permissions" because better-auth 1.4.x's `hasPermission` body schema
is a discriminated union that requires both keys present, with the
unused side set to an explicit `undefined`. zod 4 (which better-auth's
plugin uses internally) rejects "key is absent" — only "key is
undefined" passes the `z.undefined()` branch:

  z.union([
    z.object({ permission:  z.record(...), permissions: z.undefined() }),
    z.object({ permission:  z.undefined(),  permissions: z.record(...) }),
  ])

Without `permission: undefined` in the body, the schema rejects every
request with "[body] Invalid input"; the catch in canActivate turns
that into a generic 403 and customers can't load the tasks page,
findings, or any other endpoint protected by PermissionGuard.

API key requests aren't affected because they go through the
scope-checking path earlier in the guard, which is why this only
showed up for cookie sessions on customer accounts.

Test pins the body shape so a future refactor that drops the field
gets caught.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
comp-framework-editor Ready Ready Preview, Comment May 5, 2026 4:33pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
app Skipped Skipped May 5, 2026 4:33pm
portal Skipped Skipped May 5, 2026 4:33pm

Request Review

@Marfuen Marfuen merged commit 46a0112 into main May 5, 2026
11 checks passed
@Marfuen Marfuen deleted the mariano/fix-evidence-visibility branch May 5, 2026 16:34
@claudfuen
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.43.1 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants