Skip to content

Commit df775e0

Browse files
committed
New builds + deeper mfa/password-less integration options + logic fixes + updated readme.
1 parent e28da6c commit df775e0

File tree

6 files changed

+117
-19
lines changed

6 files changed

+117
-19
lines changed

README.md

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ auth = Auth(
8181
This code initializes the package.
8282
The mail arguments are not required, but needed to use verification code functionality.
8383
The `blocking` argument is optional and defaults to `True`. If set to `True`, it enables user blocking functionality.
84-
All methods return True or False with additional detailed outcome reports.
84+
All methods return True or False (unless the method is meant to return data) with additional detailed outcome reports (as in the following format):
85+
{
86+
"success": True/False,
87+
"message": "specific message or error code"
88+
}
8589

8690
## Function Reference - auth.example_func(args)
8791

@@ -103,6 +107,12 @@ All functions return a dictionary: `{"success": True/False, "message": "specific
103107
- `password` (`str`): User's password.
104108
- `custom_data` (`any`, optional): Additional user info to store. If None, defaults to an empty dictionary.
105109

110+
- **register_user_no_pass(email, custom_data=None)**
111+
- Registers a user without a password and sends a verification code via email.
112+
- **Parameters:**
113+
- `email` (`str`): User's email address.
114+
- `custom_data` (`any`, optional): Additional user info to store. If None, defaults to an empty dictionary.
115+
106116
- **verify_user(email, code)**
107117
- Verifies a user by checking the provided verification code.
108118
- **Parameters:**
@@ -116,6 +126,19 @@ All functions return a dictionary: `{"success": True/False, "message": "specific
116126
- **Parameters:**
117127
- `email` (`str`): User's email address.
118128
- `password` (`str`): User's password.
129+
- `mfa` (`bool`, optional): If set to `True`, it will send the user a six-digit code to their email for multi-factor authentication. Defaults to `False`.
130+
-
131+
- **verify_mfa_code(email, code)**
132+
- Verifies the multi-factor authentication code sent to the user's email. Can be used in conjunction with register_user_no_pass(), verify_user(), and generate_code() for passwordless sign-in.
133+
- **Parameters:**
134+
- `email` (`str`): User's email address.
135+
- `code` (`str`): Six-digit code sent to the user's email.
136+
137+
### MFA Code Management
138+
- **generate_code(email)**
139+
- Generates and emails a code to the user. Call before password and email resets or when signing in without password.
140+
- **Parameters:**
141+
- `email` (`str`): User's email address.
119142

120143
### Password Management
121144

@@ -126,11 +149,6 @@ All functions return a dictionary: `{"success": True/False, "message": "specific
126149
- `old_password` (`str`): User's current password.
127150
- `new_password` (`str`): New password to set.
128151

129-
- **generate_reset_code(email)**
130-
- Generates and emails a password reset code to the user.
131-
- **Parameters:**
132-
- `email` (`str`): User's email address.
133-
134152
- **verify_reset_code_and_reset_password(email, reset_code, new_password)**
135153
- Verifies a password reset code and resets the user's password.
136154
- **Parameters:**
@@ -148,7 +166,7 @@ All functions return a dictionary: `{"success": True/False, "message": "specific
148166
- `password` (`str`): User's password.
149167

150168
- **verify_reset_code_and_change_email(email, reset_code, new_email, password=None)**
151-
- Changes the user's email address after verifying a reset code sent to their email. Optionally verifies the password.
169+
- Changes the user's email address after verifying a reset code sent to their email. Optionally uses password verification if the user has a saved password or one is provided.
152170
- **Parameters:**
153171
- `email` (`str`): User's current email address.
154172
- `reset_code` (`str`): Reset code sent to the user's email.
@@ -188,14 +206,20 @@ When a user is blocked, they cannot log in or perform any actions that require a
188206
### Custom User Data
189207
Custom user data is a flexible field that can store any type of data. It is stored alongside the normal user data.
190208
Store all custom data in a dictionary format for more storage and to use the 2nd and 4th functions in the section below.
209+
If the method is meant to return data, it will do so in the following format:
210+
211+
{
212+
"success": True/False,
213+
"message": "Custom user data if success OR error code if failure"
214+
}
191215

192216
- **get_cust_usr_data(email)**
193-
- Retrieves all custom user data for the user.
217+
- Returns all custom user data for the user.
194218
- **Parameters:**
195219
- `email` (`str`): User's email address.
196220

197221
- **get_some_cust_usr_data(email, field)**
198-
- Retrieves a specific dictionary entry from the user's custom data. REQUIRES the custom data to be stored in a dictionary format.
222+
- Returns a specific dictionary entry from the user's custom data. REQUIRES the custom data to be stored in a dictionary format.
199223
- **Parameters:**
200224
- `email` (`str`): User's email address.
201225
- `field` (`str`): Dictionary name to retrieve.
-23.6 KB
Binary file not shown.
24.5 KB
Binary file not shown.

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
setup(
1313
name="easy_mongodb_auth_handler",
14-
version="1.0.8",
14+
version="1.0.9",
1515
description="A user authentication and verification system using MongoDB.",
1616
author="Lukbrew25",
1717
long_description=long_description,

src/easy_mongodb_auth_handler/auth.py

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,50 @@ def register_user(self, email, password, custom_data=None):
250250
except Exception as error:
251251
return {"success": False, "message": str(error)}
252252

253+
def register_user_no_pass(self, email, custom_data=None):
254+
"""
255+
registers a user without password and instead uses email verification.
256+
257+
Args:
258+
email (str): User's email address.
259+
custom_data: Custom data to save with the user.
260+
261+
Returns:
262+
dict: Success status and message.
263+
"""
264+
if custom_data is None:
265+
custom_data = {}
266+
if not self.mail_info:
267+
raise ValueError("Mail server information is required for user verification.")
268+
try:
269+
if not validate_email(email):
270+
return {"success": False, "message": self.messages["invalid_email"]}
271+
if self.users.find_one({"email": email}):
272+
return {"success": False, "message": self.messages["user_exists"]}
273+
274+
if self.blocking:
275+
blocked_user = self._find_blocked_user(email)
276+
if blocked_user:
277+
if blocked_user["blocked"]:
278+
return {"success": False, "message": self.messages["user_blocked"]}
279+
else:
280+
self.blocked.insert_one({"email": email, "blocked": False})
281+
verification_code = generate_secure_code()
282+
send_verification_email(self.mail_info, email, verification_code)
283+
self.users.insert_one(
284+
{
285+
"email": email,
286+
"password": None,
287+
"verification_code": verification_code,
288+
"blocked": False,
289+
"verified": False,
290+
"custom_data": custom_data
291+
}
292+
)
293+
return {"success": True, "message": self.messages["verification_code_sent"]}
294+
except Exception as error:
295+
return {"success": False, "message": str(error)}
296+
253297
def verify_user(self, email, code):
254298
"""
255299
verifies a user's email using a verification code.
@@ -273,13 +317,14 @@ def verify_user(self, email, code):
273317
except Exception as error:
274318
return {"success": False, "message": str(error)}
275319

276-
def authenticate_user(self, email, password):
320+
def authenticate_user(self, email, password, mfa=False):
277321
"""
278322
authenticates a user
279323
280324
Args:
281325
email (str): User's email address.
282326
password (str): User's password.
327+
mfa (bool): Enable multi-factor authentication.
283328
284329
Returns:
285330
dict: Success status and message.
@@ -292,11 +337,40 @@ def authenticate_user(self, email, password):
292337
if not user["verified"]:
293338
return {"success": False, "message": self.messages["not_verified"]}
294339
if check_password(user, password):
340+
if mfa:
341+
verification_code = generate_secure_code()
342+
self.users.find_one_and_update(
343+
{"email": email},
344+
{"$set": {"verification_code": verification_code}}
345+
)
346+
send_verification_email(self.mail_info, email, verification_code)
295347
return {"success": True, "message": self.messages["authentication_success"]}
296348
return {"success": False, "message": self.messages["invalid_creds"]}
297349
except Exception as error:
298350
return {"success": False, "message": str(error)}
299351

352+
def verify_mfa_code(self, email, code):
353+
"""
354+
verifies a user's MFA code
355+
356+
Args:
357+
email (str): User's email address.
358+
code (str): MFA code.
359+
360+
Returns:
361+
dict: Success status and message.
362+
"""
363+
try:
364+
user = self._find_user(email)
365+
output = self._block_checker(email, user)
366+
if output:
367+
return output
368+
if user["verification_code"] == code:
369+
return {"success": True, "message": self.messages["mfa_success"]}
370+
return {"success": False, "message": self.messages["invalid_mfa_code"]}
371+
except Exception as error:
372+
return {"success": False, "message": str(error)}
373+
300374
def delete_user(self, email, password, del_from_blocking=True):
301375
"""
302376
deletes a user account
@@ -332,9 +406,9 @@ def delete_user(self, email, password, del_from_blocking=True):
332406
except Exception as error:
333407
return {"success": False, "message": str(error)}
334408

335-
def generate_reset_code(self, email):
409+
def generate_code(self, email):
336410
"""
337-
Generates a password reset code and sends it to the user's email.
411+
Generates a code and sends it to the user's email.
338412
339413
Args:
340414
email (str): User's email address.
@@ -350,7 +424,7 @@ def generate_reset_code(self, email):
350424
return {"success": False, "message": self.messages["user_not_found"]}
351425

352426
reset_code = generate_secure_code()
353-
self.users.update_one({"email": email}, {"$set": {"reset_code": reset_code}})
427+
self.users.update_one({"email": email}, {"$set": {"verification_code": reset_code}})
354428
send_verification_email(self.mail_info, email, reset_code)
355429
return {"success": True, "message": self.messages["verification_code_sent"]}
356430
except Exception as error:
@@ -372,12 +446,12 @@ def verify_reset_code_and_reset_password(self, email, reset_code, new_password):
372446
user = self.users.find_one({"email": email})
373447
if not user:
374448
return {"success": False, "message": self.messages["user_not_found"]}
375-
if user.get("reset_code") != reset_code:
449+
if user.get("verification_code") != reset_code:
376450
return {"success": False, "message": self.messages["invalid_reset"]}
377451

378452
hashed_password = hash_password(new_password)
379453
self.users.update_one(
380-
{"email": email}, {"$set": {"password": hashed_password, "reset_code": None}}
454+
{"email": email}, {"$set": {"password": hashed_password, "verification_code": None}}
381455
)
382456
return {"success": True, "message": self.messages["password_reset_success"]}
383457
except Exception as error:
@@ -403,14 +477,14 @@ def verify_reset_code_and_change_email(self, email, reset_code, new_email, passw
403477
return {"success": False, "message": self.messages["user_not_found"]}
404478
if self.users.find_one({"email": new_email}):
405479
return {"success": False, "message": self.messages["user_exists"]}
406-
if user.get("reset_code") != reset_code:
480+
if user.get("verification_code") != reset_code:
407481
return {"success": False, "message": self.messages["invalid_reset"]}
408-
if password:
482+
if password or user_info.get("password"):
409483
if not check_password(user_info, password):
410484
return {"success": False, "message": self.messages["invalid_pass"]}
411485

412486
self.users.update_one(
413-
{"email": email}, {"$set": {"email": new_email, "reset_code": None}}
487+
{"email": email}, {"$set": {"email": new_email, "verification_code": None}}
414488
)
415489
return {"success": True, "message": self.messages["success"]}
416490
except Exception as error:

0 commit comments

Comments
 (0)