Skip to content

feat: enable microsoft sign ups#28080

Open
Ryukemeister wants to merge 115 commits intomainfrom
microsoft-login-flow
Open

feat: enable microsoft sign ups#28080
Ryukemeister wants to merge 115 commits intomainfrom
microsoft-login-flow

Conversation

@Ryukemeister
Copy link
Contributor

@Ryukemeister Ryukemeister commented Feb 19, 2026

What does this PR do?

Summary by cubic

Adds Microsoft (Azure AD) sign-in/sign-up behind a feature flag and refreshes the login/signup UI. On first Microsoft sign-in, auto-installs the Office 365 default calendar and syncs the user’s profile photo while keeping avatar URLs stable. Also fixes a regression that could set profileId to null on first OAuth login.

  • New Features

    • Azure AD provider gated by OUTLOOK_LOGIN_ENABLED; uses MS_GRAPH_CLIENT_ID/SECRET and MICROSOFT_CALENDAR_SCOPES.
    • Login/Signup: “Microsoft” buttons; provider route maps “microsoft” → signIn("azure-ad"); remembers “microsoft”; preserves callbackUrl; decorative icons use empty alt; adds microsoft-logo.svg; data-testid="login-subtitle".
    • Auto-installs Office 365 calendar when scopes are granted and selects the user’s default calendar via Microsoft Graph.
    • Syncs profile photo via Microsoft Graph; preserves avatar objectKey; only sets avatarUrl if it’s missing.
    • Identity: adds AZUREAD enum; maps "azure-ad" ↔ AZUREAD for account linking and viewer lookup; supports org auto-linking by email domain.
    • Tests: adds Azure AD onboarding and updates e2e tests to use data-testid selectors.
  • Migration

    • Set MS_GRAPH_CLIENT_ID and MS_GRAPH_CLIENT_SECRET; enable with OUTLOOK_LOGIN_ENABLED=true.
    • Run Prisma migration to add AZUREAD to IdentityProvider.

Written for commit 74753dc. Summary will update on new commits.

Updates since last revision

Addressed PR review feedback (hbjORbj's comments):

  • Removed non-null assertions (!): OUTLOOK_CLIENT_ID! / OUTLOOK_CLIENT_SECRET! replaced with a guard clause (if (... && OUTLOOK_CLIENT_ID && OUTLOOK_CLIENT_SECRET)); account.access_token! replaced with a conditional check.
  • Removed as any casts: (profile as any)?.email_verified and (profile as any)?.xms_edov replaced with Record<string, unknown> typed variable.
  • Centralized Outlook env constants: Moved OUTLOOK_CLIENT_ID, OUTLOOK_CLIENT_SECRET, IS_OUTLOOK_LOGIN_ENABLED from packages/features/auth/lib/outlook.ts into @calcom/lib/constants alongside MICROSOFT_CALENDAR_SCOPES. outlook.ts now re-exports from constants.
  • Added explanatory comment for getNextAuthProviderName usage in get.handler.ts (AZUREAD enum → "azure-ad" provider name, which .toLowerCase() would not produce).

Latest fix (Cubic AI feedback — confidence 9/10)

  • Restored database-derived profileId in OAuth JWT (next-auth-options.ts): A prior refactor changed determineProfile's destructuring to only capture upId, dropping the profile id. This caused profileId in the JWT to fall back to token.profileId ?? null, which is null on first OAuth login or when the profile switcher is disabled. Fixed by destructuring id from determineProfile and restoring the priority chain: visitorProfileId ?? token.profileId ?? null.

Link to Devin run: https://app.devin.ai/sessions/1c553264210348f5a8bdd9c8d15afde1
Requested by: bot_apk (apk@cognition.ai)

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 — env vars are documented in .env.example.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  1. Environment variables required:
    • MS_GRAPH_CLIENT_ID — Azure AD app client ID
    • MS_GRAPH_CLIENT_SECRET — Azure AD app client secret
    • OUTLOOK_LOGIN_ENABLED=true
  2. Visit /auth/login — a "Microsoft" button should appear alongside Google (if enabled).
  3. Click Microsoft → redirected to Azure AD consent → redirected back, user created/linked.
  4. Verify profile photo syncs from Microsoft Graph on first sign-in.
  5. Verify existing Google/CAL users are not affected.
  6. Without the env vars set, no Microsoft button should appear.
  7. Test profileId regression fix: On first Microsoft OAuth login (or with profile switcher disabled), verify that the JWT contains a valid profileId (not null). Check session token or logs to confirm profileId is set correctly.

🔍 Items for human reviewer attention

  • Login/signup UI redesign is large (new BackgroundGrid, @coss/ui components, layout changes). Please verify visual correctness across light/dark modes.
  • Azure AD email verification logic (xms_edov handling) — security-sensitive. Confirm the logic correctly handles boolean, string "1", and number 1 values.
  • Avatar objectKey preservation — verify the upsert logic doesn't break if existing avatar data is corrupted or missing.
  • Identity provider mapping (AZUREAD ↔ azure-ad) — confirm getNextAuthProviderName is used consistently and correctly.
  • Type safety: Record<string, unknown> for OAuth profile claims is better than as any, but still loosely typed. Acceptable trade-off?
  • profileId regression fix — verify that determineProfile returns { id, upId } and that id is correctly used as visitorProfileId in the JWT. Confirm first-time OAuth logins receive a valid profileId.

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
  • My PR is too large (>500 lines or >10 files) and should be split into smaller PRs — Note: This PR is large due to UI redesign + auth logic + tests. Consider splitting if further changes are needed.

retrogtx and others added 30 commits April 1, 2025 13:48
…thentication adapter

- Set OUTLOOK_LOGIN_ENABLED to false in .env.example
- Refactor getServerSideProps to directly use samlTenantID and samlProductID
- Update linkAccount method in next-auth-custom-adapter for better type handling
- Remove redundant comment in next-auth-options related to Azure AD email verification
Resolved conflicts in:
- .env.example: Kept both Microsoft OAuth and Google Calendar API Key sections
- apps/web/lib/signup/getServerSideProps.tsx: Kept both getServerSession and IS_OUTLOOK_LOGIN_ENABLED imports
- apps/web/modules/signup-view.tsx: Merged Microsoft auth with upstream's posthog tracking
- packages/features/auth/lib/identityProviderNameMap.ts: Deleted (removed in upstream)
- packages/features/auth/lib/next-auth-custom-adapter.ts: Used upstream's refactored helper functions
- packages/features/auth/lib/next-auth-options.ts: Merged Microsoft auth with upstream's billing service integration
- turbo.json: Added AZURE_AD_TENANT_ID, removed APP_USER_NAME per upstream

Co-Authored-By: unknown <>
@github-actions github-actions bot marked this pull request as draft February 23, 2026 09:45
@Ryukemeister Ryukemeister dismissed hbjORbj’s stale review February 24, 2026 06:02

implemented feedback

@Ryukemeister Ryukemeister marked this pull request as ready for review February 24, 2026 06:05
@Ryukemeister Ryukemeister requested a review from hbjORbj February 24, 2026 06:05
@Ryukemeister Ryukemeister added ready-for-e2e run-ci Approve CI to run for external contributors labels Feb 24, 2026
@hbjORbj hbjORbj marked this pull request as draft February 25, 2026 13:01
Copy link
Contributor

@hbjORbj hbjORbj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had a discussion with Rajiv off github. He will implement some improvements.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/features/auth/lib/next-auth-options.ts">

<violation number="1" location="packages/features/auth/lib/next-auth-options.ts:914">
P1: `profileId` regression: the database-derived profile ID from `determineProfile` is no longer used. On first OAuth login (or when profile switcher is disabled), `token.profileId` is likely null, so this sets `profileId: null` in the JWT even though `determineProfile` returned a valid profile with an `id`. The previous code `profileResult.id ?? token.profileId ?? null` correctly prioritized the determined profile's ID.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@github-actions
Copy link
Contributor

Devin AI is addressing Cubic AI's review feedback

A Devin session has been created to address the issues identified by Cubic AI.

View Devin Session

…h JWT

The profileId regression was identified by Cubic AI (confidence 9/10).

Previously, determineProfile's returned id was used to set profileId in the
JWT via 'profileResult.id ?? token.profileId ?? null'. A recent refactor
changed this to 'token.profileId ?? null', which drops the database-derived
profile ID. On first OAuth login (or when profile switcher is disabled),
token.profileId is likely null, so profileId would incorrectly be set to
null even though determineProfile returned a valid profile with an id.

This commit restores the correct priority chain:
  visitorProfileId ?? token.profileId ?? null

Co-Authored-By: bot_apk <apk@cognition.ai>
@devin-ai-integration
Copy link
Contributor

Fixed the profileId regression identified by Cubic AI (confidence 9/10).

Root cause: The refactor changed determineProfile's destructuring from capturing both upId and id to only capturing upId. This meant profileId was set to token.profileId ?? null instead of using the database-derived profile ID from determineProfile.

Fix: Restored the original priority chain by destructuring id from determineProfile and using it:

const { upId, id: visitorProfileId } = determineProfile({ profiles: allProfiles, token });
// ...
profileId: visitorProfileId ?? token.profileId ?? null,

This ensures that on first OAuth login (or when profile switcher is disabled), the JWT correctly receives the database-derived profile ID rather than null.

devin-ai-integration bot and others added 3 commits February 26, 2026 10:31
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
The determineProfile function returned a union type where one branch used
'id' and the other used 'profileId'. This caused TS2339 when destructuring
'id' from the result. Normalize the token.upId branch to also return 'id'
(mapped from token.profileId) so the return type is consistent.

Co-Authored-By: bot_apk <apk@cognition.ai>
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 6 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/web/modules/signup-view.tsx">

<violation number="1">
P1: Custom agent: **Avoid Logging Sensitive Information**

Avoid logging PII in analytics events. This GTM event includes `email` and `user` (username), which violates the rule against logging sensitive information. Remove or anonymize these fields before sending.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

Devin AI is addressing Cubic AI's review feedback

New feedback has been sent to the existing Devin session.

View Devin Session


✅ No changes pushed — the signup-view.tsx issue (PII in GTM analytics events) has confidence 8/10, below the 9/10 threshold.

@Ryukemeister Ryukemeister marked this pull request as ready for review February 26, 2026 13:33
@Ryukemeister Ryukemeister dismissed hbjORbj’s stale review February 26, 2026 13:33

updated the changes discussed

@Ryukemeister Ryukemeister requested a review from hbjORbj February 26, 2026 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

consumer core area: core, team members only ❗️ .env changes contains changes to env variables ❗️ migrations contains migration files ready-for-e2e run-ci Approve CI to run for external contributors size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants