From d7041c0efd2ce1b87a3a095562e01f10a707642d Mon Sep 17 00:00:00 2001 From: Khushboo Verma <43381712+vermakhushboo@users.noreply.github.com> Date: Wed, 6 Dec 2023 22:38:38 +0100 Subject: [PATCH] WIP: Add security phrase to magic URL --- .../locale/templates/email-inner-base.tpl | 2 + app/controllers/api/account.php | 38 ++++++++++++++++++- src/Appwrite/Utopia/Response/Model/Token.php | 6 +++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/app/config/locale/templates/email-inner-base.tpl b/app/config/locale/templates/email-inner-base.tpl index 64f1302569..eda6ede786 100644 --- a/app/config/locale/templates/email-inner-base.tpl +++ b/app/config/locale/templates/email-inner-base.tpl @@ -4,6 +4,8 @@
{{body}}
+{{securityPhrase}} + {{redirect}} {{code}} diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 168eb0886d..8d3e915c12 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -1016,6 +1016,7 @@ App::post('/v1/account/sessions/magic-url') ->param('email', '', new Email(), 'User email.') ->param('url', '', fn($clients) => new Host($clients), 'URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) ->param('type', 'link', new WhiteList(['link', 'code']), 'The type of verification email to be sent. ', true) + ->param('securityPhrase', false, new Boolean(), 'Security phrase to verify that the user is the one who initiated the action. This phrase will be sent to the user email address.', true) ->inject('request') ->inject('response') ->inject('user') @@ -1024,7 +1025,7 @@ App::post('/v1/account/sessions/magic-url') ->inject('locale') ->inject('queueForEvents') ->inject('queueForMails') - ->action(function (string $userId, string $email, string $url, string $type, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { + ->action(function (string $userId, string $email, string $url, string $type, string $securityPhrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled'); @@ -1097,6 +1098,35 @@ App::post('/v1/account/sessions/magic-url') $expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM)); } + if ($securityPhrase) { + // create two arrays of random words from the dictionary and pick one word from each array to generate a security phrase + $wordArray1 = array( + 'Apple', 'Banana', 'Cherry', 'Dragon', 'Elephant', 'Flower', 'Giraffe', 'Harmony', 'Island', 'Journey', 'Kiwi', 'Lemon', 'Mango', 'Nebula', 'Ocean', + 'Panda', 'Quasar', 'Rainbow', 'Sunshine', 'Tiger', + 'Umbrella', 'Violet', 'Whale', 'Xylophone', 'Yoga', + 'Zeppelin', 'Breeze', 'Cascade', 'Dolphin', 'Enigma', + 'Fountain', 'Galaxy', 'Horizon', 'Ivory', 'Jubilee', + 'Kaleidoscope', 'Lagoon', 'Midnight', 'Nova', 'Opulence', + 'Pebble', 'Quasar', 'Radiance', 'Sapphire', 'Tranquil', + 'Utopia', 'Vivid', 'Willow', 'Zenith' + ); + + $wordArray2 = array( + 'Aurora', 'Bliss', 'Cerulean', 'Dusk', 'Eclipse', + 'Fiesta', 'Grace', 'Harmony', 'Infinite', 'Jubilant', + 'Kaleidoscope', 'Lullaby', 'Mystic', 'Nirvana', 'Orchid', + 'Purity', 'Quintessence', 'Ripple', 'Serene', 'Tranquil', + 'Utopia', 'Vibrant', 'Whisper', 'Xanadu', 'Yonder', + 'Zephyr', 'Bamboo', 'Cascade', 'Dewdrop', 'Ethereal', + 'Fandango', 'Gossamer', 'Harmony', 'Inferno', 'Jade', + 'Kismet', 'Luminous', 'Majestic', 'Nebula', 'Opulent', + 'Paradise', 'Quasar', 'Radiant', 'Serenade', 'Talisman', + 'Uplift', 'Velvet', 'Wanderlust', 'Zest' + ); + + $securityPhraseString = $wordArray1[array_rand($wordArray1)] . $wordArray2[array_rand($wordArray2)]; + } + $token = new Document([ '$id' => ID::unique(), 'userId' => $user->getId(), @@ -1131,6 +1161,10 @@ App::post('/v1/account/sessions/magic-url') $subject = $locale->getText("emails.magicSession.subject"); $customTemplate = $project->getAttribute('templates', [])['email.magicSession-' . $locale->default] ?? []; + if ($securityPhrase) { + $body = $body . "