{"record":{"id":"d3d5e0c9726ebb63","repo":"BookStackApp/BookStack","slug":"errors-auth-pre-register-theme-prevention","errorCode":null,"errorMessage":"errors.auth_pre_register_theme_prevention","messagePattern":"errors\\.auth_pre_register_theme_prevention","errorType":"exception","errorClass":"UserRegistrationException","httpStatus":null,"severity":"error","filePath":"app/Access/RegistrationService.php","lineNumber":93,"sourceCode":"     */\n    public function registerUser(array $userData, ?SocialAccount $socialAccount = null, bool $emailConfirmed = false): User\n    {\n        $userEmail = $userData['email'];\n        $authSystem = $socialAccount ? $socialAccount->driver : auth()->getDefaultDriver();\n\n        // Email restriction\n        $this->ensureEmailDomainAllowed($userEmail);\n\n        // Ensure the user does not already exist\n        $alreadyUser = !is_null($this->userRepo->getByEmail($userEmail));\n        if ($alreadyUser) {\n            throw new UserRegistrationException(trans('errors.error_user_exists_different_creds', ['email' => $userEmail]), '/login');\n        }\n\n        /** @var ?bool $shouldRegister */\n        $shouldRegister = Theme::dispatch(ThemeEvents::AUTH_PRE_REGISTER, $authSystem, $userData);\n        if ($shouldRegister === false) {\n            throw new UserRegistrationException(trans('errors.auth_pre_register_theme_prevention'), '/login');\n        }\n\n        // Create the user\n        $newUser = $this->userRepo->createWithoutActivity($userData, $emailConfirmed);\n        $newUser->attachDefaultRole();\n\n        // Assign a social account if given\n        if ($socialAccount) {\n            $newUser->socialAccounts()->save($socialAccount);\n        }\n\n        Activity::add(ActivityType::AUTH_REGISTER, $socialAccount ?? $newUser);\n        Theme::dispatch(ThemeEvents::AUTH_REGISTER, $authSystem, $newUser);\n\n        // Start the email confirmation flow if required\n        if ($this->emailConfirmationService->confirmationRequired() && !$emailConfirmed) {\n            $newUser->save();\n","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/BookStackApp/BookStack/blob/18f8469a1c72f8cc8497e9372635e6dea5028071/app/Access/RegistrationService.php#L75-L111","documentation":"registerUser() dispatches the AUTH_PRE_REGISTER theme event, allowing custom theme code to veto registration by returning exactly false. When that happens, BookStack throws UserRegistrationException with the translated 'errors.auth_pre_register_theme_prevention' message and aborts user creation.","triggerScenarios":"registerUser() (via findOrRegister during OIDC/external login) fires Theme::dispatch(ThemeEvents::AUTH_PRE_REGISTER, $authSystem, $userData); the registered theme handler returns boolean false, causing the exception before user creation.","commonSituations":"A custom theme's AUTH_PRE_REGISTER hook (e.g. allowlisting domains, blocking bots, syncing to external systems) deliberately rejects the user, or the hook has a bug returning falsy/false unintentionally (e.g. returning 0, null-cast logic, or returning false from an API-check helper).","solutions":["Inspect your theme's AUTH_PRE_REGISTER handler (functions.php / theme code) to see why it returned false","Log inputs ($authSystem, $userData) inside the handler to identify the rejected user and reason","Fix the handler's logic if the rejection is unintended (e.g. domain allowlist missing the user's domain)","If intentional, communicate the block to users or adjust the allowlist; ensure the handler returns true/void (not false) to allow registration"],"exampleFix":"// before (theme functions.php — vetoes everything accidentally)\nTheme::listen(ThemeEvents::AUTH_PRE_REGISTER, function ($system, $data) {\n    return checkAllowed($data['email']); // returns false on lookup failure\n});\n// after — only veto explicitly, default to allow\nTheme::listen(ThemeEvents::AUTH_PRE_REGISTER, function ($system, $data) {\n    if (str_ends_with($data['email'], '@blocked.example.com')) {\n        return false;\n    }\n    return true;\n});","handlingStrategy":"try-catch","validationCode":"// Audit all theme listeners for AUTH_PRE_REGISTER and ensure they only return false deliberately:\n$listeners = Theme::getListeners(ThemeEvents::AUTH_PRE_REGISTER); // if exposed\n// Or grep your themes dir:\n// grep -rn \"AUTH_PRE_REGISTER\" themes/","typeGuard":null,"tryCatchPattern":"try {\n    auth()->attemptOidcLogin();\n} catch (BookStack\\Access\\Oidc\\OidcException $e) {\n    if (str_contains($e->getMessage(), 'auth_pre_register_theme_prevention')) {\n        Log::info('Registration vetoed by theme hook', ['user' => $email]);\n        return redirect('/login')->withErrors(['theme' => 'Registration was blocked by a custom policy']);\n    }\n    throw $e;\n}","preventionTips":["Return true (or nothing) from AUTH_PRE_REGISTER handlers to allow registration","Log inside theme hooks so vetoes are traceable","Test theme hooks with fresh users before deploying","Guard async lookups in hooks so transient failures don't return false"],"tags":["themes","registration","hooks"],"backgroundTag":"theme-hook-prevented-registration","analyzedSha":"18f8469a1c72f8cc8497e9372635e6dea5028071","analyzedAt":"2026-09-02T19:49:33.068Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}