Skip to content

Commit

Permalink
Added PasswordResetToken model and CRUD operations
Browse files Browse the repository at this point in the history
  • Loading branch information
gaurav-jo1 committed Oct 11, 2024
1 parent b5fc332 commit 9672dd6
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 10 deletions.
3 changes: 2 additions & 1 deletion frontend/src/components/auth/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,12 @@ const LoginForm = () => {
onClick={() => navigate("/forgot-password")}
variant="link"
className="justify-start px-1 mt-2"
type="button"
>
Forgot Password?
</Button>
{/* Submit Button */}
<Button variant="primary" className="mt-2">
<Button variant="primary" className="mt-2" type="submit">
Login
</Button>
</form>
Expand Down
21 changes: 16 additions & 5 deletions store/app/crud/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from typing import List

from store.app.crud.base import BaseCrud
from store.app.model import EmailSignUpToken
from store.app.model import EmailSignUpToken, PasswordResetToken


class EmailCrud(BaseCrud):
Expand All @@ -19,15 +19,26 @@ async def get_email_signup_token(self, id: str) -> EmailSignUpToken | None:
async def delete_email_signup_token(self, id: str) -> None:
await self._delete_item(id)

async def remove_existing_token_for_email(self, email: str) -> None:
user_tokens: List[EmailSignUpToken] = await self._get_items_from_secondary_index(
"email", email, EmailSignUpToken
async def create_password_reset_token(self, email: str) -> PasswordResetToken:
reset_token = PasswordResetToken.create(email=email)
await self._add_item(reset_token)
return reset_token

async def get_password_reset_token(self, id: str) -> PasswordResetToken | None:
return await self._get_item(id, PasswordResetToken, throw_if_missing=False)

async def delete_password_reset_token(self, id: str) -> None:
await self._delete_item(id)

async def delete_password_reset_token_by_email(self, email: str) -> None:
user_tokens: List[PasswordResetToken] = await self._get_items_from_secondary_index(
"email", email, PasswordResetToken
)

if not user_tokens:
return

delete_tasks = [self.delete_email_signup_token(token.id) for token in user_tokens]
delete_tasks = [self._delete_item(token.id) for token in user_tokens]
await asyncio.gather(*delete_tasks)


Expand Down
13 changes: 13 additions & 0 deletions store/app/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,19 @@ def create(cls, email: str) -> Self:
return cls(id=new_uuid(), email=email)


class PasswordResetToken(StoreBaseModel):
"""Object created when user requests a password reset.
Used to validate and authorize password reset requests.
"""

email: str

@classmethod
def create(cls, email: str) -> Self:
return cls(id=new_uuid(), email=email)


class OAuthKey(StoreBaseModel):
"""Keys for OAuth providers which identify users."""

Expand Down
8 changes: 4 additions & 4 deletions store/app/routers/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,8 +399,8 @@ async def generate_password_reset_token(
) -> ForgotPasswordResponse:
try:
if user := await crud.get_user_from_email(data.email):
await crud.remove_existing_token_for_email(user.email)
reset_token = await crud.create_email_signup_token(email=user.email)
await crud.delete_password_reset_token_by_email(user.email)
reset_token = await crud.create_password_reset_token(email=user.email)

await send_reset_password_email(email=user.email, token=reset_token.id)
logger.info(f"Password reset email sent to {user.email}")
Expand All @@ -424,7 +424,7 @@ class ResetPasswordResponse(BaseModel):
async def validate_password_reset_token(
data: ResetPasswordRequest, crud: Annotated[Crud, Depends(Crud.get)]
) -> ResetPasswordResponse:
reset_token = await crud.get_email_signup_token(data.token)
reset_token = await crud.get_password_reset_token(data.token)
if not reset_token:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Invalid or expired reset token")

Expand All @@ -439,6 +439,6 @@ async def validate_password_reset_token(
)

# Remove reset token
await crud.delete_email_signup_token(data.token)
await crud.delete_password_reset_token(data.token)

return ResetPasswordResponse(message="Password updated successful", email=updated_user.email)

0 comments on commit 9672dd6

Please sign in to comment.