From 393170c773228ceebd125c18427b40edf437bab1 Mon Sep 17 00:00:00 2001 From: Konstantin Wohlwend Date: Fri, 22 Aug 2025 13:48:05 -0700 Subject: [PATCH] When sign ups are disabled, account linking should continue to work --- .../oauth/callback/[provider_id]/route.tsx | 8 +- .../api/v1/auth/oauth/merge-strategy.test.ts | 120 +++++++++++++++++- 2 files changed, 122 insertions(+), 6 deletions(-) diff --git a/apps/backend/src/app/api/latest/auth/oauth/callback/[provider_id]/route.tsx b/apps/backend/src/app/api/latest/auth/oauth/callback/[provider_id]/route.tsx index b12479d3b..641139093 100644 --- a/apps/backend/src/app/api/latest/auth/oauth/callback/[provider_id]/route.tsx +++ b/apps/backend/src/app/api/latest/auth/oauth/callback/[provider_id]/route.tsx @@ -276,10 +276,6 @@ const handler = createSmartRouteHandler({ // ========================== sign up user ========================== - if (!tenancy.config.auth.allowSignUp) { - throw new KnownErrors.SignUpNotEnabled(); - } - let primaryEmailAuthEnabled = false; if (userInfo.email) { primaryEmailAuthEnabled = true; @@ -352,6 +348,10 @@ const handler = createSmartRouteHandler({ } } + if (!tenancy.config.auth.allowSignUp) { + throw new KnownErrors.SignUpNotEnabled(); + } + const newAccount = await usersCrudHandlers.adminCreate({ tenancy, data: { diff --git a/apps/e2e/tests/backend/endpoints/api/v1/auth/oauth/merge-strategy.test.ts b/apps/e2e/tests/backend/endpoints/api/v1/auth/oauth/merge-strategy.test.ts index bb8e41e49..cda21dcb9 100644 --- a/apps/e2e/tests/backend/endpoints/api/v1/auth/oauth/merge-strategy.test.ts +++ b/apps/e2e/tests/backend/endpoints/api/v1/auth/oauth/merge-strategy.test.ts @@ -1,5 +1,5 @@ -import { it } from "../../../../../../helpers"; -import { Auth, ContactChannels, InternalApiKey, Project } from "../../../../../backend-helpers"; +import { it, updateCookiesFromResponse } from "../../../../../../helpers"; +import { Auth, ContactChannels, InternalApiKey, Project, backendContext, niceBackendFetch } from "../../../../../backend-helpers"; it("should allow duplicates, if the merge strategy is set to allow_duplicates", async ({ expect }) => { const proj = await Project.createAndSwitch({ @@ -134,3 +134,119 @@ it("should not merge accounts if the merge strategy is set to link_method, but t } `); }); + +it("should allow OAuth login with manually created account when sign-ups are disabled", async ({ expect }) => { + // Create a project with sign-ups disabled and OAuth provider configured + const proj = await Project.createAndSwitch({ + config: { + sign_up_enabled: false, + oauth_account_merge_strategy: "link_method", + oauth_providers: [{ + id: "spotify", + type: "shared", + }], + }, + }); + await InternalApiKey.createAndSetProjectKeys(proj.adminAccessToken); + + // Get the default mailbox email that will be used by mock OAuth + const spotifyMockEmail = backendContext.value.mailbox.emailAddress; + + // Manually create a user account with that email address with auth enabled + const createUserResponse = await niceBackendFetch("/api/v1/users", { + method: "POST", + accessType: "admin", + body: { + primary_email: spotifyMockEmail, + primary_email_verified: true, + primary_email_auth_enabled: true, + display_name: "Manual User", + }, + }); + + expect(createUserResponse).toMatchInlineSnapshot(` + NiceResponse { + "status": 201, + "body": { + "auth_with_email": false, + "client_metadata": null, + "client_read_only_metadata": null, + "display_name": "Manual User", + "has_password": false, + "id": "", + "is_anonymous": false, + "last_active_at_millis": , + "oauth_providers": [], + "otp_auth_enabled": false, + "passkey_auth_enabled": false, + "primary_email": "default-mailbox--@stack-generated.example.com", + "primary_email_auth_enabled": true, + "primary_email_verified": true, + "profile_image_url": null, + "requires_totp_mfa": false, + "selected_team": null, + "selected_team_id": null, + "server_metadata": null, + "signed_up_at_millis": , + }, + "headers": Headers {