Skip to content

Commit 393170c

Browse files
committed
When sign ups are disabled, account linking should continue to work
1 parent 301398f commit 393170c

2 files changed

Lines changed: 122 additions & 6 deletions

File tree

apps/backend/src/app/api/latest/auth/oauth/callback/[provider_id]/route.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,6 @@ const handler = createSmartRouteHandler({
276276

277277
// ========================== sign up user ==========================
278278

279-
if (!tenancy.config.auth.allowSignUp) {
280-
throw new KnownErrors.SignUpNotEnabled();
281-
}
282-
283279
let primaryEmailAuthEnabled = false;
284280
if (userInfo.email) {
285281
primaryEmailAuthEnabled = true;
@@ -352,6 +348,10 @@ const handler = createSmartRouteHandler({
352348
}
353349
}
354350

351+
if (!tenancy.config.auth.allowSignUp) {
352+
throw new KnownErrors.SignUpNotEnabled();
353+
}
354+
355355
const newAccount = await usersCrudHandlers.adminCreate({
356356
tenancy,
357357
data: {

apps/e2e/tests/backend/endpoints/api/v1/auth/oauth/merge-strategy.test.ts

Lines changed: 118 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { it } from "../../../../../../helpers";
2-
import { Auth, ContactChannels, InternalApiKey, Project } from "../../../../../backend-helpers";
1+
import { it, updateCookiesFromResponse } from "../../../../../../helpers";
2+
import { Auth, ContactChannels, InternalApiKey, Project, backendContext, niceBackendFetch } from "../../../../../backend-helpers";
33

44
it("should allow duplicates, if the merge strategy is set to allow_duplicates", async ({ expect }) => {
55
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
134134
}
135135
`);
136136
});
137+
138+
it("should allow OAuth login with manually created account when sign-ups are disabled", async ({ expect }) => {
139+
// Create a project with sign-ups disabled and OAuth provider configured
140+
const proj = await Project.createAndSwitch({
141+
config: {
142+
sign_up_enabled: false,
143+
oauth_account_merge_strategy: "link_method",
144+
oauth_providers: [{
145+
id: "spotify",
146+
type: "shared",
147+
}],
148+
},
149+
});
150+
await InternalApiKey.createAndSetProjectKeys(proj.adminAccessToken);
151+
152+
// Get the default mailbox email that will be used by mock OAuth
153+
const spotifyMockEmail = backendContext.value.mailbox.emailAddress;
154+
155+
// Manually create a user account with that email address with auth enabled
156+
const createUserResponse = await niceBackendFetch("/api/v1/users", {
157+
method: "POST",
158+
accessType: "admin",
159+
body: {
160+
primary_email: spotifyMockEmail,
161+
primary_email_verified: true,
162+
primary_email_auth_enabled: true,
163+
display_name: "Manual User",
164+
},
165+
});
166+
167+
expect(createUserResponse).toMatchInlineSnapshot(`
168+
NiceResponse {
169+
"status": 201,
170+
"body": {
171+
"auth_with_email": false,
172+
"client_metadata": null,
173+
"client_read_only_metadata": null,
174+
"display_name": "Manual User",
175+
"has_password": false,
176+
"id": "<stripped UUID>",
177+
"is_anonymous": false,
178+
"last_active_at_millis": <stripped field 'last_active_at_millis'>,
179+
"oauth_providers": [],
180+
"otp_auth_enabled": false,
181+
"passkey_auth_enabled": false,
182+
"primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
183+
"primary_email_auth_enabled": true,
184+
"primary_email_verified": true,
185+
"profile_image_url": null,
186+
"requires_totp_mfa": false,
187+
"selected_team": null,
188+
"selected_team_id": null,
189+
"server_metadata": null,
190+
"signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
191+
},
192+
"headers": Headers { <some fields may have been hidden> },
193+
}
194+
`);
195+
196+
const createdUserId = createUserResponse.body.id;
197+
198+
// Now try to sign in via OAuth with the same email
199+
// This should succeed even though sign-ups are disabled
200+
// because we're linking to an existing account with matching email
201+
const { authorizeResponse, innerCallbackUrl } = await Auth.OAuth.getInnerCallbackUrl();
202+
const cookie = updateCookiesFromResponse("", authorizeResponse);
203+
204+
const oauthCallbackResponse = await niceBackendFetch(innerCallbackUrl.toString(), {
205+
redirect: "manual",
206+
headers: {
207+
cookie,
208+
},
209+
});
210+
211+
const { tokenResponse } = await Auth.OAuth.signIn();
212+
expect(tokenResponse.body.is_new_user).toBe(false);
213+
214+
const getUserResponse = await niceBackendFetch(`/api/v1/users/${createdUserId}`, {
215+
method: "GET",
216+
accessType: "admin",
217+
});
218+
expect(getUserResponse).toMatchInlineSnapshot(`
219+
NiceResponse {
220+
"status": 200,
221+
"body": {
222+
"auth_with_email": false,
223+
"client_metadata": null,
224+
"client_read_only_metadata": null,
225+
"display_name": "Manual User",
226+
"has_password": false,
227+
"id": "<stripped UUID>",
228+
"is_anonymous": false,
229+
"last_active_at_millis": <stripped field 'last_active_at_millis'>,
230+
"oauth_providers": [
231+
{
232+
"account_id": "default-mailbox--<stripped UUID>@stack-generated.example.com",
233+
"email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
234+
"id": "spotify",
235+
},
236+
],
237+
"otp_auth_enabled": false,
238+
"passkey_auth_enabled": false,
239+
"primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
240+
"primary_email_auth_enabled": true,
241+
"primary_email_verified": true,
242+
"profile_image_url": null,
243+
"requires_totp_mfa": false,
244+
"selected_team": null,
245+
"selected_team_id": null,
246+
"server_metadata": null,
247+
"signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
248+
},
249+
"headers": Headers { <some fields may have been hidden> },
250+
}
251+
`);
252+
});

0 commit comments

Comments
 (0)