Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Login Endpoint #148

Merged
merged 11 commits into from
Jan 27, 2024
2 changes: 1 addition & 1 deletion backend/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,4 @@ INSERT INTO species(name) VALUES ('unknown');
/*
* Inserts test user into user table
*/
INSERT INTO users(username, email, pword, admin) VALUES ('test', 'garvina@oregonstate.edu', 'password', '1');
INSERT INTO users(username, email, pword, admin) VALUES ('test', 'garvina@oregonstate.edu', '$2b$12$2Z74k3vqzchWB..McZbdUOp4BXd6RGsWcS0atZJfVVheGexvH7i0O', '1');
38 changes: 36 additions & 2 deletions backend/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ fastapi = "^0.104.0"
psycopg = "^3.1.12"
psycopg-pool = "^3.1.8"
biopython = "^1.81"
PyJWT = "^2.8.0"
passlib = "^1.7.4"


[tool.poetry.group.dev.dependencies]
Expand Down
41 changes: 41 additions & 0 deletions backend/src/api/users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from fastapi import APIRouter
import logging as log
from passlib.hash import bcrypt
from ..api_types import LoginBody, LoginError, ResponseToken
from ..db import Database
from ..auth import generateAuthToken

router = APIRouter()


@router.post("/users/login", response_model=ResponseToken | LoginError)
def login(body: LoginBody):
with Database() as db:
try:
email = body.email
password = body.password

query = (
"""SELECT users.pword, users.admin FROM users WHERE users.email = %s;"""
)
entry_sql = db.execute_return(query, [email])

# Returns "incorrect email/password" message if there is no such account.
if entry_sql is None or len(entry_sql) == 0:
return LoginError.INCORRECT

# Grabs the stored hash and admin.
password_hash, admin = entry_sql[0]

# Returns "incorrect email/password" message if password is incorrect.
if not bcrypt.verify(password, password_hash):
return LoginError.INCORRECT

# Generates the token and returns
token = generateAuthToken(email, admin)
return ResponseToken(token=token)

except Exception as e:
log.error(e)
# TODO: Return something better than query error
return LoginError.QUERY_ERROR
16 changes: 16 additions & 0 deletions backend/src/api_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,19 @@ class EditBody(CamelModel):
new_species_name: str
new_content: str | None = None
new_refs: str | None = None


class LoginBody(CamelModel):
email: str
password: str


class LoginError(str, enum.Enum):
DEBUG_ACCOUNT = "Debug: Account Not Found"
DEBUG_PASSWORD = "Debug: Incorrect password"
INCORRECT = "Invalid Email or Password"
QUERY_ERROR = "QUERY_ERROR"


class ResponseToken(CamelModel):
token: str
27 changes: 27 additions & 0 deletions backend/src/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import jwt
from datetime import datetime, timezone, timedelta

# TODO: This method of secret key generation is, obviously, extremely unsafe.
# This needs to be changed.
secret_key = "SuperSecret"


def generateAuthToken(userId, admin):
payload = {
"email": userId,
"admin": admin,
"exp": datetime.now(tz=timezone.utc) + timedelta(hours=24),
}
return jwt.encode(payload, secret_key, algorithm="HS256")


# TODO: Find out how FastAPI handles middleware functions, and turn this into one.
def authenticateToken(token):
# Return the decoded token if it's valid.
try:
decoded = jwt.decode(token, secret_key, algorithms="HS256")
return decoded

# If the token is invalid, return None.
except Exception:
return None
3 changes: 3 additions & 0 deletions backend/src/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
from .db import Database, bytea_to_str, str_to_bytea
from .protein import parse_protein_pdb, pdb_file_name, protein_name_found, pdb_to_fasta
from .setup import disable_cors, init_fastapi_app
from .api import users


app = init_fastapi_app()
disable_cors(app, origins=[os.environ["PUBLIC_FRONTEND_URL"]])

app.include_router(users.router)


@app.get("/pdb/{protein_name:str}")
def get_pdb_file(protein_name: str):
Expand Down
Loading