Skip to content

Commit

Permalink
partial email/pass login logic
Browse files Browse the repository at this point in the history
  • Loading branch information
Winston-Hsiao committed Aug 10, 2024
1 parent 8af0e57 commit 304ad37
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 19 deletions.
23 changes: 20 additions & 3 deletions frontend/src/components/auth/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { SubmitHandler, useForm } from "react-hook-form";

import { zodResolver } from "@hookform/resolvers/zod";
import { useAlertQueue } from "hooks/useAlertQueue";
import { useAuthentication } from "hooks/useAuth";
import { LoginSchema, LoginType } from "types";

import { Button } from "components/ui/Button/Button";
Expand All @@ -18,11 +19,27 @@ const LoginForm = () => {
resolver: zodResolver(LoginSchema),
});

const { addAlert } = useAlertQueue();
const { addAlert, addErrorAlert } = useAlertQueue();
const auth = useAuthentication();

const onSubmit: SubmitHandler<LoginType> = async (data: LoginType) => {
// TODO: Add an api endpoint to send the credentials details to backend and email verification.
addAlert(`Not yet implemented: ${data.email}`, "success");
try {
const { data: response, error } = await auth.client.POST("/users/login", {
body: data,
});

if (error) {
addErrorAlert(error);
} else {
addAlert(`Login successful! Welcome, back!`, "success");

// Successful Login
// TODO: authenticated login state
console.log(JSON.stringify(response));
}
} catch {
addErrorAlert("An unexpected error occurred during login.");
}
};

return (
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/components/auth/SignupForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ const SignupForm: React.FC<SignupFormProps> = ({ signupTokenId }) => {
return (
<form
onSubmit={handleSubmit(onSubmit)}
className="grid grid-cols-1 space-y-6">
className="grid grid-cols-1 space-y-6"
>
{/* Email Input */}
<div className="relative">
<Input placeholder="Email" type="text" {...register("email")} />
Expand Down Expand Up @@ -106,7 +107,8 @@ const SignupForm: React.FC<SignupFormProps> = ({ signupTokenId }) => {
{/* Signup Button */}
<Button
variant="outline"
className="w-full text-white bg-blue-600 hover:bg-opacity-70">
className="w-full text-white bg-blue-600 hover:bg-opacity-70"
>
Sign up
</Button>
</form>
Expand Down
12 changes: 8 additions & 4 deletions frontend/src/components/footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,24 @@ const Footer = () => {
href="https://www.linkedin.com/company/kscale"
ariaLabel="Visit K-Scale's LinkedIn Page"
bgColor={LinkedinPrimaryColor}
ringColor="focus:ring-sky-500">
ringColor="focus:ring-sky-500"
>
<FaLinkedinIn />
</SocialLink>
<SocialLink
href="https://github.com/kscalelabs/store"
ariaLabel="Visit K-Scale's Github Page"
bgColor={GithubPrimaryColor}
ringColor="focus:ring-black">
ringColor="focus:ring-black"
>
<FaGithub />
</SocialLink>
<SocialLink
href="https://discord.gg/rhCy6UdBRD"
ariaLabel="Join K-Scale's Discord"
bgColor={DiscordPrimaryColor}
ringColor="focus:ring-black">
ringColor="focus:ring-black"
>
<FaDiscord />
</SocialLink>
</div>
Expand All @@ -60,7 +63,8 @@ const Footer = () => {
href="https://kscale.dev/about/"
className="hover:text-gray-500"
target="_blank"
rel="noopener noreferrer">
rel="noopener noreferrer"
>
About us
</a>
<a href="" className="hover:text-gray-500">
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/ui/Input/PasswordInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ const PasswordInput = <T extends FieldValues>({
<div className="mt-2 text-sm text-gray-600 dark:text-gray-400">
Password Strength:{" "}
<span
className={`font-semibold text-${getStrengthColor(passwordStrength)}`}>
className={`font-semibold text-${getStrengthColor(passwordStrength)}`}
>
{getStrengthLabel(passwordStrength)}
</span>
</div>
Expand Down
67 changes: 67 additions & 0 deletions frontend/src/gen/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,23 @@ export interface paths {
patch?: never;
trace?: never;
};
"/users/login": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
/** Login User */
post: operations["login_user_users_login_post"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/users/batch": {
parameters: {
query?: never;
Expand Down Expand Up @@ -574,6 +591,23 @@ export interface components {
/** Image Url */
image_url: string | null;
};
/** LoginRequest */
LoginRequest: {
/**
* Email
* Format: email
*/
email: string;
/** Password */
password: string;
};
/** LoginResponse */
LoginResponse: {
/** User Id */
user_id: string;
/** Token */
token: string;
};
/** NewListingRequest */
NewListingRequest: {
/** Name */
Expand Down Expand Up @@ -769,6 +803,39 @@ export interface operations {
};
};
};
login_user_users_login_post: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody: {
content: {
"application/json": components["schemas"]["LoginRequest"];
};
};
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["LoginResponse"];
};
};
/** @description Validation Error */
422: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
get_users_batch_endpoint_users_batch_get: {
parameters: {
query: {
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/pages/Register.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ const Register = () => {
className="w-full text-white bg-blue-600 hover:bg-opacity-70"
onClick={() => {
navigate("/login");
}}>
}}
>
Login / Signup
</Button>
</div>
Expand Down
44 changes: 36 additions & 8 deletions store/app/routers/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
from fastapi.security.utils import get_authorization_scheme_param
from pydantic import EmailStr
from pydantic.main import BaseModel as PydanticBaseModel

from store.app.crud.base import ItemNotFoundError
Expand All @@ -16,6 +17,8 @@
from store.app.model import User, UserPermission
from store.app.routers.auth.github import github_auth_router
from store.app.utils.email import send_delete_email
from store.app.utils.password import verify_password
from store.utils import new_uuid

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -133,7 +136,9 @@ class SendRegister(BaseModel):


class UserRegister(BaseModel):
token: str
signup_token_id: str
email: str
password: str


class UserInfoResponse(BaseModel):
Expand Down Expand Up @@ -182,16 +187,10 @@ class PublicUserInfoResponse(BaseModel):
users: list[SinglePublicUserInfoResponseItem]


class UserRegister(BaseModel):
signup_token_id: str
email: str
password: str


@users_router.post("/register", response_model=SinglePublicUserInfoResponseItem)
async def register_user(
data: UserRegister, email_signup_crud: EmailSignUpCrud = Depends(), user_crud: UserCrud = Depends()
):
) -> SinglePublicUserInfoResponseItem: # Added return type annotation
async with email_signup_crud, user_crud:
signup_token = await email_signup_crud.get_email_signup_token(data.signup_token_id)
if not signup_token:
Expand All @@ -208,6 +207,35 @@ async def register_user(
return SinglePublicUserInfoResponseItem(id=user.id, email=user.email)


class LoginRequest(BaseModel):
email: EmailStr
password: str


class LoginResponse(BaseModel):
user_id: str
token: str


@users_router.post("/login", response_model=LoginResponse)
async def login_user(
data: LoginRequest, user_crud: UserCrud = Depends()
) -> LoginResponse: # Added return type annotation
async with user_crud:
# Fetch user by email
user = await user_crud.get_user_from_email(data.email)
if not user:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid email or password")

# Verify password
if not verify_password(data.password, user.hashed_password):
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid email or password")

token = new_uuid()

return LoginResponse(user_id=user.id, token=token)


@users_router.get("/batch", response_model=PublicUserInfoResponse)
async def get_users_batch_endpoint(
crud: Annotated[Crud, Depends(Crud.get)],
Expand Down

0 comments on commit 304ad37

Please sign in to comment.