Repository: edgedb/edgedb

Issue:
EdgeDB Auth: Resetting your password logs you in even if you haven't verified your email
- EdgeDB Version: 4.1 and nightly

---

If you have the `require_verification` configuration value set, you are able to still log in if you use the password reset flow.


Discussion:
One discussed fix here is to actually treat the reset flow as a verification flow since it _does_ verify that you control the email.

Resolving patch:
diff --git a/edb/server/protocol/auth_ext/local.py b/edb/server/protocol/auth_ext/local.py
--- a/edb/server/protocol/auth_ext/local.py
+++ b/edb/server/protocol/auth_ext/local.py
@@ -297,7 +297,10 @@ async def update_password(
   new_hash := <str>$new_hash,
 update ext::auth::EmailPasswordFactor
 filter .identity.id = identity_id
-set { password_hash := new_hash };""",
+set {
+    password_hash := new_hash,
+    verified_at := .verified_at ?? datetime_current()
+};""",
             variables={
                 'identity_id': identity_id,
                 'new_hash': ph.hash(password),


Test patch:
diff --git a/tests/test_http_ext_auth.py b/tests/test_http_ext_auth.py
--- a/tests/test_http_ext_auth.py
+++ b/tests/test_http_ext_auth.py
@@ -1967,7 +1967,7 @@ async def test_http_auth_ext_local_password_register_form_02(self):
                 headers={"Content-Type": "application/x-www-form-urlencoded"},
             )
 
-            self.assertEquals(status, 400)
+            self.assertEqual(status, 400)
 
     async def test_http_auth_ext_local_password_register_json_02(self):
         with self.http_con() as http_con:
@@ -2611,7 +2611,7 @@ async def test_http_auth_ext_local_password_forgot_form_02(self):
                 body=form_data_encoded,
                 headers={"Content-Type": "application/x-www-form-urlencoded"},
             )
-            self.assertEquals(status, 400)
+            self.assertEqual(status, 400)
 
     async def test_http_auth_ext_local_password_reset_form_01(self):
         with self.http_con() as http_con:
@@ -2634,6 +2634,15 @@ async def test_http_auth_ext_local_password_reset_form_01(self):
                 body=form_data_encoded,
                 headers={"Content-Type": "application/x-www-form-urlencoded"},
             )
+            email_password_factor = await self.con.query_single(
+                """
+                with module ext::auth
+                SELECT EmailPasswordFactor { verified_at }
+                FILTER .email = <str>$email
+                """,
+                email=form_data["email"],
+            )
+            self.assertIsNone(email_password_factor.verified_at)
 
             # Send reset
             verifier = base64.urlsafe_b64encode(os.urandom(32)).rstrip(b'=')
@@ -2711,6 +2720,17 @@ async def test_http_auth_ext_local_password_reset_form_01(self):
 
             self.assertEqual(len(identity), 1)
 
+            email_password_factor = await self.con.query_single(
+                """
+                with module ext::auth
+                SELECT EmailPasswordFactor { verified_at }
+                FILTER .identity.id = <uuid>$identity_id
+                """,
+                identity_id=identity[0].id,
+            )
+
+            self.assertIsNotNone(email_password_factor.verified_at)
+
             pkce_challenge = await self.con.query_single(
                 """
                 with module ext::auth