Skip to content

Conversation

@PeerRich
Copy link
Member

@PeerRich PeerRich commented Jan 7, 2026

What does this PR do?

Shows a TopBanner to free users (individuals not in an organization) whose email domain matches a verified OrganizationDomain. The banner prompts them to join the organization with a "Join team" CTA button.

When the user clicks "Join team":

  • A toast notification shows "Owner notified"
  • An email is sent to all admins and owners of the organization notifying them that a user wants to join

Key changes:

  • New OrgJoinBanner component integrated into the banner system
  • New tRPC query checkForVerifiedOrgDomain to detect matching organizations
  • New tRPC mutation requestOrgMembership to handle the join request and send emails
  • New email template OrganizationJoinRequestEmail for admin notifications
  • Two new repository methods in OrganizationRepository for data access
  • i18n translation keys for all user-facing strings
  • Fix to lint-staged.config.mjs to exclude locale files from biome format check (biome was configured to ignore public/ directories but lint-staged was still trying to format them)

Updates since last revision

Security fix: Added emailVerified check to both handlers to prevent attackers from creating accounts with unverified emails (e.g., attacker@company.com) and triggering join request emails to org admins without ever verifying ownership of that email address.

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. N/A - no docs changes needed.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  1. Create a verified organization with orgAutoAcceptEmail set to a domain (e.g., acme.com)
  2. Create a free user (not in any organization) with an email matching that domain (e.g., user@acme.com)
  3. Verify the user's email (banner should NOT appear for unverified emails)
  4. Log in as the free user
  5. Verify the banner appears: "An organization for acme.com exists. Would you like to join it?"
  6. Click "Join team"
  7. Verify toast shows "Owner notified"
  8. Check that org admins/owners received the notification email

Security test: Create a user with an unverified email matching an org domain - verify the banner does NOT appear and the join request mutation returns a FORBIDDEN error.

Human Review Checklist

  • Verify the banner only appears for users NOT already in an organization
  • Verify the emailVerified check prevents unverified email abuse (security-critical)
  • Verify email domain matching logic is correct (extracts domain from user email and compares to orgAutoAcceptEmail)
  • Review the lint-staged config change - it excludes all files in /public/static/locales/ from biome format check
  • Verify email template renders correctly with all translation keys

Checklist

  • My code follows the style guidelines of this project
  • I have commented my code, particularly in hard-to-understand areas
  • I have checked if my changes generate no new warnings

Link to Devin run: https://app.devin.ai/sessions/6f9977d40aa74a20890c8318f3434e2f
Requested by: @PeerRich

- Show TopBanner for free users whose email domain has a verified OrganizationDomain
- Banner title: 'An organization for {domain} exists. Would you like to join it?'
- CTA button: 'Join team' that shows toast 'Owner notified'
- Send email to all org admins/owners when user clicks 'Join team'
- Add email template for organization join request notification
- Use repository pattern for database access

Co-Authored-By: peer@cal.com <peer@cal.com>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

devin-ai-integration bot and others added 3 commits January 7, 2026 17:08
- Show TopBanner for free users whose email domain has a verified OrganizationDomain
- Banner title: 'An organization for {domain} exists. Would you like to join it?'
- CTA button: 'Join team' that shows toast 'Owner notified'
- Send email to all org admins/owners when user clicks 'Join team'
- Add email template for organization join request notification
- Use repository pattern for database access

Co-Authored-By: peer@cal.com <peer@cal.com>
Also fix lint-staged config to exclude locale files from biome format check,
since biome is configured to ignore public/ directories but lint-staged was
still trying to format them, causing commit failures.

Co-Authored-By: peer@cal.com <peer@cal.com>
@vercel
Copy link

vercel bot commented Jan 7, 2026

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

4 Skipped Deployments
Project Deployment Review Updated (UTC)
api-v2 Ignored Ignored Preview Jan 7, 2026 7:48pm
cal Ignored Ignored Jan 7, 2026 7:48pm
cal-companion Ignored Ignored Preview Jan 7, 2026 7:48pm
cal-eu Ignored Ignored Jan 7, 2026 7:48pm

Addresses security concern where an attacker could create an account with
an unverified email (e.g., attacker@company.com) and trigger join request
emails to org admins without ever verifying ownership of that email.

Both handlers now check user.emailVerified before:
- Showing the organization join banner
- Allowing the user to request membership

Co-Authored-By: peer@cal.com <peer@cal.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core area: core, team members only size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants