From 132f84702d679b55724b94deef023ec499d0ff19 Mon Sep 17 00:00:00 2001 From: Gavin Chait Date: Sun, 26 Feb 2023 15:57:50 +0100 Subject: [PATCH] Fixed login artifacts The original login system allowed a person to login and validate their email afterwards, but the magic approach means that this is no longer valid. Some artefacts remained, picked up by @FranzForstmayr, and this should remove them all. --- .../app/app/api/api_v1/endpoints/login.py | 5 +- .../app/app/api/api_v1/endpoints/users.py | 43 ----------------- .../backend/app/app/crud/crud_user.py | 3 +- .../frontend/api/auth.ts | 17 ------- .../settings/ValidateEmailButton.vue | 25 ---------- .../frontend/pages/settings.vue | 34 +++++--------- .../frontend/stores/auth.ts | 46 ------------------- 7 files changed, 16 insertions(+), 157 deletions(-) delete mode 100644 {{cookiecutter.project_slug}}/frontend/components/settings/ValidateEmailButton.vue diff --git a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/login.py b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/login.py index a319c4eda9..1ac54e0f00 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/login.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/login.py @@ -1,4 +1,5 @@ from typing import Any, Union, Dict +from pydantic import EmailStr from fastapi import APIRouter, Body, Depends, HTTPException from fastapi.security import OAuth2PasswordRequestForm @@ -33,14 +34,14 @@ @router.post("/magic/{email}", response_model=schemas.WebToken) -def login_with_magic_link(*, db: Session = Depends(deps.get_db), email: str) -> Any: +def login_with_magic_link(*, db: Session = Depends(deps.get_db), email: EmailStr) -> Any: """ First step of a 'magic link' login. Check if the user exists and generate a magic link. Generates two short-duration jwt tokens, one for validation, one for email. Creates user if not exist. """ user = crud.user.get_by_email(db, email=email) if not user: - user_in = schemas.UserCreate(**{email: email}) + user_in = schemas.UserCreate(**{"email": email}) user = crud.user.create(db, obj_in=user_in) if not crud.user.is_active(user): # Still permits a timed-attack, but does create ambiguity. diff --git a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/users.py b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/users.py index 65be1cb5a7..caae0bd6ff 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/users.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/users.py @@ -10,7 +10,6 @@ from app.core.config import settings from app.core import security from app.utilities import ( - send_email_validation_email, send_new_account_email, ) @@ -111,48 +110,6 @@ def request_new_totp( return obj_in -# @router.post("/send-validation-email", response_model=schemas.Msg, status_code=201) -# def send_validation_email( -# *, -# current_user: models.User = Depends(deps.get_current_active_user), -# ) -> Any: -# """ -# Send validation email. -# """ -# password_validation_token = generate_password_reset_token(email=current_user.email) -# data = schemas.EmailValidation( -# **{ -# "email": current_user.email, -# "subject": "Validate your email address", -# "token": password_validation_token, -# } -# ) -# # EmailValidation -# send_email_validation_email(data=data) -# return {"msg": "Password validation email sent. Check your email and respond."} - - -@router.post("/validate-email", response_model=schemas.Msg) -def validate_email( - *, - db: Session = Depends(deps.get_db), - payload: dict = Body(...), - current_user: models.User = Depends(deps.get_current_active_user), -) -> Any: - """ - Reset password - """ - # https://stackoverflow.com/a/65114346/295606 - email = verify_password_reset_token(payload["validation"]) - if not email or current_user.email != email: - raise HTTPException( - status_code=400, - detail="Invalid token", - ) - crud.user.validate_email(db=db, db_obj=current_user) - return {"msg": "Email address validated successfully."} - - @router.post("/toggle-state", response_model=schemas.Msg) def toggle_state( *, diff --git a/{{cookiecutter.project_slug}}/backend/app/app/crud/crud_user.py b/{{cookiecutter.project_slug}}/backend/app/app/crud/crud_user.py index ec230f3e01..d77a31265f 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/crud/crud_user.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/crud/crud_user.py @@ -16,10 +16,11 @@ def get_by_email(self, db: Session, *, email: str) -> Optional[User]: def create(self, db: Session, *, obj_in: UserCreate) -> User: db_obj = User( email=obj_in.email, - hashed_password=get_password_hash(obj_in.password), full_name=obj_in.full_name, is_superuser=obj_in.is_superuser, ) + if obj_in.password: + db_obj.hashed_password = get_password_hash(obj_in.password) db.add(db_obj) db.commit() db.refresh(db_obj) diff --git a/{{cookiecutter.project_slug}}/frontend/api/auth.ts b/{{cookiecutter.project_slug}}/frontend/api/auth.ts index 174303bb06..8e1de1ac39 100644 --- a/{{cookiecutter.project_slug}}/frontend/api/auth.ts +++ b/{{cookiecutter.project_slug}}/frontend/api/auth.ts @@ -146,23 +146,6 @@ export const apiAuth = { } ) }, - async requestValidationEmail(token: string) { - return await useFetch(`${apiCore.url()}/users/send-validation-email`, - { - method: "POST", - headers: apiCore.headers(token) - } - ) - }, - async validateEmail(token: string, validation: string) { - return await useFetch(`${apiCore.url()}/users/validate-email`, - { - method: "POST", - body: { validation }, - headers: apiCore.headers(token) - } - ) - }, // ADMIN USER MANAGEMENT async getAllUsers(token: string) { return await useFetch(`${apiCore.url()}/users/all`, diff --git a/{{cookiecutter.project_slug}}/frontend/components/settings/ValidateEmailButton.vue b/{{cookiecutter.project_slug}}/frontend/components/settings/ValidateEmailButton.vue deleted file mode 100644 index de659eb9a0..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/components/settings/ValidateEmailButton.vue +++ /dev/null @@ -1,25 +0,0 @@ - - - \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/pages/settings.vue b/{{cookiecutter.project_slug}}/frontend/pages/settings.vue index 27eb28f0ec..df8dfbb73e 100644 --- a/{{cookiecutter.project_slug}}/frontend/pages/settings.vue +++ b/{{cookiecutter.project_slug}}/frontend/pages/settings.vue @@ -4,34 +4,22 @@
diff --git a/{{cookiecutter.project_slug}}/frontend/stores/auth.ts b/{{cookiecutter.project_slug}}/frontend/stores/auth.ts index ae6110b09f..5484e5086e 100644 --- a/{{cookiecutter.project_slug}}/frontend/stores/auth.ts +++ b/{{cookiecutter.project_slug}}/frontend/stores/auth.ts @@ -191,52 +191,6 @@ export const useAuthStore = defineStore("authUser", { this.password = payload.password this.totp = payload.totp }, - async sendEmailValidation() { - await this.authTokens.refreshTokens() - if (this.authTokens.token && !this.email_validated) { - try { - const { data: response } = await apiAuth.requestValidationEmail(this.authTokens.token) - if (response.value) { - toasts.addNotice({ - title: "Validation sent", - content: response.value.msg, - }) - } - } catch (error) { - toasts.addNotice({ - title: "Validation error", - content: "Please check your email and try again.", - icon: "error" - }) - } - } - }, - async validateEmail(validationToken: string) { - await this.authTokens.refreshTokens() - if (this.authTokens.token && !this.email_validated) { - try { - const { data: response } = await apiAuth.validateEmail( - this.authTokens.token, - validationToken - ) - if (response.value) { - this.email_validated = true - if (response.value) { - toasts.addNotice({ - title: "Success", - content: response.value.msg, - }) - } - } - } catch (error) { - toasts.addNotice({ - title: "Validation error", - content: "Invalid token. Check your email and resend validation.", - icon: "error" - }) - } - } - }, async recoverPassword(email: string) { if (!this.loggedIn) { try {