diff --git a/src/backend/controllers/auth/AuthController.js b/src/backend/controllers/auth/AuthController.js index 0568cd511..bff21250a 100644 --- a/src/backend/controllers/auth/AuthController.js +++ b/src/backend/controllers/auth/AuthController.js @@ -404,6 +404,7 @@ export class AuthController extends PuterController { no_temp_user: false, requires_email_confirmation: false, message: null, + code: null, }; try { await this.clients.event?.emitAndWait( @@ -418,6 +419,11 @@ export class AuthController extends PuterController { throw new HttpError( 403, validateEvent.message ?? 'Signup blocked', + { + ...(validateEvent.code + ? { code: validateEvent.code } + : {}), + }, ); } if (is_temp && validateEvent.no_temp_user) { @@ -425,7 +431,12 @@ export class AuthController extends PuterController { 403, validateEvent.message ?? 'Temporary accounts are disabled', - { legacyCode: 'must_login_or_signup' }, + { + legacyCode: 'must_login_or_signup', + ...(validateEvent.code + ? { code: validateEvent.code } + : {}), + }, ); } const force_email_confirmation = Boolean( diff --git a/src/backend/controllers/auth/AuthController.test.ts b/src/backend/controllers/auth/AuthController.test.ts new file mode 100644 index 000000000..9eed45f2b --- /dev/null +++ b/src/backend/controllers/auth/AuthController.test.ts @@ -0,0 +1,114 @@ +/** + * Copyright (C) 2024-present Puter Technologies Inc. + * + * This file is part of Puter. + * + * Puter is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import { PuterServer } from '../../server.js'; +import { setupTestServer } from '../../testUtil.js'; +import type { EventClient } from '../../clients/EventClient.js'; +import { HttpError } from '../../core/http/HttpError.js'; + +let server: PuterServer; +let eventClient: EventClient; + +beforeAll(async () => { + server = await setupTestServer(); + eventClient = server.clients.event as unknown as EventClient; +}); + +afterAll(async () => { + await server?.shutdown(); +}); + +describe('puter.signup.validate event', () => { + it('supports code in the validate event when allow is false', async () => { + eventClient.on('puter.signup.validate', (_key, data) => { + const event = data as { + allow: boolean; + message: string | null; + code: string | null; + }; + event.allow = false; + event.message = 'Region not supported'; + event.code = 'region_blocked'; + }); + + const validateEvent = { + req: {}, + data: {}, + ip: '127.0.0.1', + email: 'test@example.com', + allow: true, + no_temp_user: false, + requires_email_confirmation: false, + message: null as string | null, + code: null as string | null, + }; + + await eventClient.emitAndWait( + 'puter.signup.validate', + validateEvent, + {}, + ); + + expect(validateEvent.allow).toBe(false); + expect(validateEvent.message).toBe('Region not supported'); + expect(validateEvent.code).toBe('region_blocked'); + + // Verify the HttpError constructed from this event carries the code + const err = new HttpError( + 403, + validateEvent.message ?? 'Signup blocked', + { + ...(validateEvent.code + ? { code: validateEvent.code } + : {}), + }, + ); + expect(err.statusCode).toBe(403); + expect(err.message).toBe('Region not supported'); + expect(err.code).toBe('region_blocked'); + }); + + it('omits code from HttpError when extension does not set it', async () => { + const validateEvent = { + req: {}, + data: {}, + ip: '127.0.0.1', + email: 'nocode@example.com', + allow: false, + no_temp_user: false, + requires_email_confirmation: false, + message: 'Blocked', + code: null as string | null, + }; + + const err = new HttpError( + 403, + validateEvent.message ?? 'Signup blocked', + { + ...(validateEvent.code + ? { code: validateEvent.code } + : {}), + }, + ); + expect(err.statusCode).toBe(403); + expect(err.message).toBe('Blocked'); + expect(err.code).toBeUndefined(); + }); +}); diff --git a/src/backend/services/auth/OIDCService.ts b/src/backend/services/auth/OIDCService.ts index 068caa894..9350b2a0c 100644 --- a/src/backend/services/auth/OIDCService.ts +++ b/src/backend/services/auth/OIDCService.ts @@ -407,6 +407,7 @@ export class OIDCService extends PuterService { no_temp_user: false, requires_email_confirmation: false, message: null as string | null, + code: null as string | null, }; try { await this.clients.event?.emitAndWait( @@ -421,6 +422,7 @@ export class OIDCService extends PuterService { return { success: false, error: validateEvent.message ?? 'Signup blocked', + ...(validateEvent.code ? { code: validateEvent.code } : {}), }; }