From 24f8a126cbcb8bf0b31a76bc0f74b9709570d7b5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 25 Apr 2026 16:21:46 +0000 Subject: [PATCH] feat: allow using existing deSEC account by supplying a password Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/57f233da-439b-4992-888c-82fad2dfa2cc Co-authored-by: szaimen <42591237+szaimen@users.noreply.github.com> --- php/src/Controller/DesecController.php | 7 +-- php/src/Desec/DesecManager.php | 66 +++++++++++++++++++++----- php/templates/containers.twig | 2 + 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/php/src/Controller/DesecController.php b/php/src/Controller/DesecController.php index 6b9246b4..42f5c96d 100644 --- a/php/src/Controller/DesecController.php +++ b/php/src/Controller/DesecController.php @@ -15,9 +15,10 @@ readonly class DesecController { public function Register(Request $request, Response $response, array $args): Response { try { - $email = (string)($request->getParsedBody()['desec_email'] ?? ''); - $slug = (string)($request->getParsedBody()['desec_slug'] ?? ''); - $this->desecManager->register($email, $slug); + $email = (string)($request->getParsedBody()['desec_email'] ?? ''); + $slug = (string)($request->getParsedBody()['desec_slug'] ?? ''); + $password = (string)($request->getParsedBody()['desec_password'] ?? ''); + $this->desecManager->register($email, $slug, $password); return $response->withStatus(201)->withHeader('Location', '.'); } catch (\Exception $ex) { $response->getBody()->write($ex->getMessage()); diff --git a/php/src/Desec/DesecManager.php b/php/src/Desec/DesecManager.php index 29b0d43e..c692c6ce 100644 --- a/php/src/Desec/DesecManager.php +++ b/php/src/Desec/DesecManager.php @@ -29,9 +29,14 @@ class DesecManager { * Full registration flow: validates inputs, creates an account if needed, * registers the domain, enables required containers, and updates the DNS record. * + * When $password is non-empty the user is logging into an existing deSEC account + * rather than creating a new one. When $password is empty a new account is created + * with a randomly generated password (unless an account was already registered in a + * previous attempt). + * * @throws \Exception on any validation or API error */ - public function register(string $email, string $slug): void { + public function register(string $email, string $slug, string $password = ''): void { if ($this->configurationManager->domain !== '') { throw new \Exception('A domain is already configured. Reset the AIO instance first to register a new domain.'); } @@ -41,19 +46,23 @@ class DesecManager { ? $this->configurationManager->desecToken : null; - $validatedEmail = null; - if (!$accountAlreadyRegistered) { - $validatedEmail = $this->validateEmail($email); - } - $validatedSlug = $this->validateSlug($slug); if (!$accountAlreadyRegistered) { - // 24 random bytes → 48-char hex password; satisfies deSEC's minimum length - // and lets the user log in at desec.io if they ever need to. - $password = bin2hex(random_bytes(24)); - $token = $this->registerAccount($validatedEmail, $password); - $this->saveAccountCredentials($token, $password, $validatedEmail); + $validatedEmail = $this->validateEmail($email); + $validatedPassword = trim($password); + + if ($validatedPassword !== '') { + // The user supplied their existing deSEC password — log in instead of registering. + $token = $this->loginAccount($validatedEmail, $validatedPassword); + $this->saveAccountCredentials($token, $validatedPassword, $validatedEmail); + } else { + // 24 random bytes → 48-char hex password; satisfies deSEC's minimum length + // and lets the user log in at desec.io if they ever need to. + $generatedPassword = bin2hex(random_bytes(24)); + $token = $this->registerAccount($validatedEmail, $generatedPassword); + $this->saveAccountCredentials($token, $generatedPassword, $validatedEmail); + } } $domain = $this->registerDomain($token, $validatedSlug); @@ -114,7 +123,7 @@ class DesecManager { if (is_array($data) && isset($data['email'])) { throw new \Exception( 'This email address is already registered at deSEC. ' - . 'Please log in at https://desec.io to retrieve your token and set up your domain manually.', + . 'If this is your account, please enter your deSEC password in the password field and try again.', ); } throw new \Exception('Registration at deSEC failed (HTTP 400): ' . $body); @@ -132,6 +141,39 @@ class DesecManager { return $data['token']['token']; } + /** + * Authenticates with an existing deSEC account and returns the API token issued for it. + * + * @throws \Exception on invalid credentials, network failure, or an unexpected HTTP response + */ + public function loginAccount(string $email, string $password): string { + try { + $res = $this->guzzleClient->post(self::DESEC_API_BASE . '/auth/login/', [ + 'json' => ['email' => $email, 'password' => $password], + ]); + } catch (TransferException $e) { + throw new \Exception('Could not reach the deSEC API: ' . $e->getMessage()); + } + + $code = $res->getStatusCode(); + $body = $res->getBody()->getContents(); + + if ($code === 400 || $code === 403) { + throw new \Exception('Could not log in to deSEC: invalid email address or password.'); + } + + if ($code !== 200 && $code !== 201) { + throw new \Exception('Unexpected response from deSEC during login (HTTP ' . $code . '): ' . $body); + } + + $data = json_decode($body, true, 512, JSON_THROW_ON_ERROR); + if (!is_array($data) || !isset($data['token']) || !is_string($data['token'])) { + throw new \Exception('Could not extract the API token from the deSEC login response. Please try again.'); + } + + return $data['token']; + } + /** * Registers a dedyn.io domain for the authenticated account. * When $slug is empty a random 10-character slug is tried up to MAX_SLUG_ATTEMPTS times. diff --git a/php/templates/containers.twig b/php/templates/containers.twig index ee318be9..592caa41 100644 --- a/php/templates/containers.twig +++ b/php/templates/containers.twig @@ -146,10 +146,12 @@ {% else %}
Please enter your email address. You can also enter a desired subdomain slug (the part before .dedyn.io); leave it blank for a random one.
If you already have a deSEC account for this email address, enter your deSEC password in the optional password field below to log in with it instead of creating a new account.