Skip to content

fix(auth): check all emails for domain/email access#829

Merged
ColeMurray merged 1 commit into
ColeMurray:mainfrom
CompanyCam:main
Jun 26, 2026
Merged

fix(auth): check all emails for domain/email access#829
ColeMurray merged 1 commit into
ColeMurray:mainfrom
CompanyCam:main

Conversation

@kadams54

@kadams54 kadams54 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Description

GitHub OAuth only exposes the primary email in the NextAuth user object. Users with multiple email addressses were silently denied even with a valid domain in ALLOWED_EMAIL_DOMAINS. Use cases:

  • Consultants with company/client emails
  • Anyone who uses GitHub's private emails
  • Anyone who prefers a personal email over their work email for their primary

Now the signIn callback fetches all verified emails from /user/emails (already authorized by the user:email scope) and passes the full list to getAccessAllowReason, which checks each one against the domain/email allowlists.

Summary by CodeRabbit

  • Bug Fixes
    • Sign-in and access checks now support multiple email addresses, improving allowlist matching for users with more than one verified email.
    • GitHub sign-in handling now evaluates email- and domain-based access rules using all verified GitHub emails (not just the primary).
    • Access decisions fail more consistently when provider/email verification details don’t match expected access rules.
  • Documentation
    • Updated setup guides with a new GitHub App Account permissions step (Email addresses: Read-only) before creating the app.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

GitHub App setup instructions add a read-only email permission step. Access control now evaluates multiple emails instead of one. GitHub sign-in fetches verified emails and uses them in static allowlist checks.

Changes

GitHub email access flow

Layer / File(s) Summary
Access-control array contract
packages/web/src/lib/access-control.ts, packages/web/src/lib/access-control.test.ts
AccessCheckParams now uses emails[], exact-email and domain allowlist checks iterate over multiple addresses, and the tests switch to array inputs across allowlist and OR cases.
GitHub sign-in email flow
packages/web/src/lib/auth.ts, packages/web/src/lib/auth.test.ts
GitHub sign-in fetches verified emails from GitHub when needed, passes emails[] into getStaticSignInReason, and the sign-in tests use array-based profiles.
GitHub App setup docs
.claude/skills/onboarding/SKILL.md, docs/GETTING_STARTED.md
The GitHub App setup instructions add the account-permissions step for read-only email addresses and renumber the later setup steps.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • amyragan3297

Poem

🐇 Hop, hop, the emails line up bright,
One paw on GitHub, one ear in flight.
Read-only permissions, then off we go,
Verified sign-ins in a moonlit glow.
A carrot for the trail, neat and sweet.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: auth access checks now consider all emails for domain and email allowlists.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/web/src/lib/access-control.test.ts (1)

203-226: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a multiple-email exact allowlist regression.

This block proves “any email” works for domains, but not for allowedEmails. Add the exact-email case so the core ALLOWED_EMAILS path is covered too.

Suggested test addition
   describe("when user has multiple emails", () => {
     const config = {
       allowedDomains: ["company.com"],
       allowedUsers: [],
       allowedEmails: [],
       unsafeAllowAllUsers: false,
     };

+    it("allows access when any email exactly matches the email allowlist", () => {
+      expect(
+        checkAccessAllowed(
+          { ...config, allowedDomains: [], allowedEmails: ["user@company.com"] },
+          {
+            emails: ["user@personal.com", "user@company.com"],
+          }
+        )
+      ).toBe(true);
+    });
+
     it("allows access when any email matches the domain", () => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/web/src/lib/access-control.test.ts` around lines 203 - 226, The
multiple-email coverage in checkAccessAllowed currently verifies only the
allowedDomains path, so add a regression test in the same describe block for
allowedEmails to confirm that access is granted when any email exactly matches
an entry in allowedEmails and denied when none do. Use the existing
checkAccessAllowed helper and the config shape in access-control.test.ts so the
ALLOWED_EMAILS behavior is covered alongside the domain-based case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/web/src/lib/auth.ts`:
- Around line 318-323: The GitHub email lookup in auth.ts only runs when allowed
domains are configured, so exact-email allowlists miss secondary verified GitHub
addresses. Update the email resolution logic around the fetchGitHubEmails call
in the auth flow to also fetch GitHub emails whenever ALLOWED_EMAILS is in use
for GitHub providers, then match against the returned verified emails instead of
falling back only to user.email. Keep the change localized to the existing
email-selection branch so the allowlist check can use the full GitHub email set.
- Around line 67-74: The new fetchGitHubEmails sign-in path can hang or throw,
so it should fail closed with a bounded request and error handling. Update
fetchGitHubEmails to use an explicit timeout in milliseconds via a named
constant, and catch any fetch/json/network errors so the function returns an
empty list instead of rejecting. Keep the change localized to fetchGitHubEmails
in auth.ts and make sure the timeout/default is defined once with a clear
unit-bearing name.

---

Nitpick comments:
In `@packages/web/src/lib/access-control.test.ts`:
- Around line 203-226: The multiple-email coverage in checkAccessAllowed
currently verifies only the allowedDomains path, so add a regression test in the
same describe block for allowedEmails to confirm that access is granted when any
email exactly matches an entry in allowedEmails and denied when none do. Use the
existing checkAccessAllowed helper and the config shape in
access-control.test.ts so the ALLOWED_EMAILS behavior is covered alongside the
domain-based case.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0a98a684-f465-47ee-be71-df87964d8308

📥 Commits

Reviewing files that changed from the base of the PR and between a537ba3 and 4862139.

📒 Files selected for processing (6)
  • .claude/skills/onboarding/SKILL.md
  • docs/GETTING_STARTED.md
  • packages/web/src/lib/access-control.test.ts
  • packages/web/src/lib/access-control.ts
  • packages/web/src/lib/auth.test.ts
  • packages/web/src/lib/auth.ts

Comment thread packages/web/src/lib/auth.ts Outdated
Comment thread packages/web/src/lib/auth.ts

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/web/src/lib/auth.ts`:
- Around line 291-296: The sign-in flow in auth.ts is making two identical
GitHub email lookups for the same access token, once in the profile request
handler and again in the allowlist check. Update the request handler in the auth
profile flow to fetch verified emails once and attach/reuse that result as
metadata for the sign-in step. Then change the allowlist validation logic to
consume the existing verified email data instead of calling
getVerifiedGitHubEmails again, using the request and sign-in code paths as the
coordination points.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c5e1e23e-62c5-4f58-b677-5a026def171a

📥 Commits

Reviewing files that changed from the base of the PR and between 4862139 and f99135e.

📒 Files selected for processing (6)
  • .claude/skills/onboarding/SKILL.md
  • docs/GETTING_STARTED.md
  • packages/web/src/lib/access-control.test.ts
  • packages/web/src/lib/access-control.ts
  • packages/web/src/lib/auth.test.ts
  • packages/web/src/lib/auth.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/GETTING_STARTED.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/web/src/lib/access-control.ts
  • packages/web/src/lib/access-control.test.ts

Comment thread packages/web/src/lib/auth.ts
GitHub OAuth only exposes the primary email in the NextAuth user
object. Users whose primary GitHub email is personal (not their
work address) were silently denied even with a valid domain in
ALLOWED_EMAIL_DOMAINS.

Now the signIn callback fetches all verified emails from
/user/emails (already authorized by the user:email scope) and
passes the full list to checkAccessAllowed, which checks each one
against the domain allowlist.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ColeMurray ColeMurray merged commit 73b1b09 into ColeMurray:main Jun 26, 2026
18 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.

2 participants