From 42ee9326ae95508d07116e4ff762d624c703f377 Mon Sep 17 00:00:00 2001 From: agatha Date: Sat, 6 Apr 2024 13:33:44 -0400 Subject: [PATCH 1/9] chore: remove unused import --- backend/models.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/models.py b/backend/models.py index d342728..0eaf353 100644 --- a/backend/models.py +++ b/backend/models.py @@ -1,5 +1,3 @@ -from sqlalchemy.orm import relationship - from database import Base from sqlalchemy import Column, Integer, String, ForeignKey -- 2.30.2 From 4a31a1b67108d12f88673950709dc2ec98829241 Mon Sep 17 00:00:00 2001 From: agatha Date: Sat, 6 Apr 2024 13:55:38 -0400 Subject: [PATCH 2/9] update backend .gitignore --- backend/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/.gitignore b/backend/.gitignore index 866cead..b9b8699 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1 +1,2 @@ +.idea/ forum.db -- 2.30.2 From d0e593a26876e6cdd810dfe73cfecd452745a6c3 Mon Sep 17 00:00:00 2001 From: agatha Date: Sat, 6 Apr 2024 13:58:20 -0400 Subject: [PATCH 3/9] refactor app structure --- backend/main.py | 110 +++--------------------------------- backend/routers/__init__.py | 0 backend/routers/auth.py | 8 +++ backend/routers/forum.py | 106 ++++++++++++++++++++++++++++++++++ 4 files changed, 121 insertions(+), 103 deletions(-) create mode 100644 backend/routers/__init__.py create mode 100644 backend/routers/auth.py create mode 100644 backend/routers/forum.py diff --git a/backend/main.py b/backend/main.py index bc04e52..a8de0cb 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,108 +1,12 @@ -from typing import Annotated -from fastapi import FastAPI, Depends, HTTPException, Path -from sqlalchemy.orm import Session -from pydantic import BaseModel, Field -from starlette import status +from fastapi import FastAPI -from database import engine, SessionLocal -from models import Base, Post, Thread +import models +from database import engine +from routers import auth, forum app = FastAPI() -Base.metadata.create_all(bind=engine) - - -def get_db(): - db = SessionLocal() - try: - yield db - finally: - db.close() - - -db_dependency = Annotated[Session, Depends(get_db)] - - -class PostCreate(BaseModel): - author: str = Field('anon') - title: str = Field('') - content: str = Field('') - - -@app.get('/', status_code=status.HTTP_200_OK) -async def get_posts(db: db_dependency): - return db.query(Post).all() - - -@app.post('/', status_code=status.HTTP_201_CREATED) -async def create_thread(db: db_dependency, data: PostCreate): - try: - # Create the post - post = Post( - author=data.author, - title=data.title, - content=data.content - ) - db.add(post) - db.flush() - - # Create the thread - thread = Thread( - author=post.author, - title=post.title, - content=post.content - ) - db.add(thread) - db.flush() - - # Update the Post with thread_id - post.thread_id = thread.id - db.commit() - - return { - 'id': post.id, - 'author': post.author, - 'title': post.title, - 'content': post.content - } - except Exception as e: - db.rollback() - raise HTTPException(status_code=400, detail=str(e)) - - -@app.get('/{thread_id}', status_code=status.HTTP_200_OK) -async def get_thread_by_id(db: db_dependency, thread_id: int = Path(gt=0)): - posts = db.query(Post).filter(Post.thread_id == thread_id).all() - if posts: - return posts - - raise HTTPException(404, f'Could not find thread') - - -@app.post('/{thread_id}', status_code=status.HTTP_201_CREATED) -async def create_reply(db: db_dependency, data: PostCreate, thread_id: int = Path(gt=0)): - thread = db.query(Thread).filter(Thread.id == thread_id).first() - if thread: - post = Post( - thread_id=thread_id, - author=data.author, - title=data.title, - content=data.content - ) - db.add(post) - db.commit() - - return { - 'id': post.id, - 'author': post.author, - 'title': post.title, - 'content': post.content - } - - raise HTTPException(status_code=404, detail='Could not find thread') - - -@app.get('/catalog', status_code=status.HTTP_200_OK) -async def get_catalog(db: db_dependency): - return db.query(Thread).all() +models.Base.metadata.create_all(bind=engine) +app.include_router(auth.router) +app.include_router(forum.router) diff --git a/backend/routers/__init__.py b/backend/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/routers/auth.py b/backend/routers/auth.py new file mode 100644 index 0000000..7980643 --- /dev/null +++ b/backend/routers/auth.py @@ -0,0 +1,8 @@ +from fastapi import APIRouter + +router = APIRouter() + + +@router.get('/auth/') +async def get_user(): + return {'user': 'authenticated'} diff --git a/backend/routers/forum.py b/backend/routers/forum.py new file mode 100644 index 0000000..a7fda60 --- /dev/null +++ b/backend/routers/forum.py @@ -0,0 +1,106 @@ +from typing import Annotated +from fastapi import APIRouter, Depends, HTTPException, Path +from sqlalchemy.orm import Session +from pydantic import BaseModel, Field +from starlette import status + +from database import SessionLocal +from models import Post, Thread + +router = APIRouter() + + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + + +db_dependency = Annotated[Session, Depends(get_db)] + + +class PostCreate(BaseModel): + author: str = Field('anon') + title: str = Field('') + content: str = Field('') + + +@router.get('/', status_code=status.HTTP_200_OK) +async def get_posts(db: db_dependency): + return db.query(Post).all() + + +@router.post('/', status_code=status.HTTP_201_CREATED) +async def create_thread(db: db_dependency, data: PostCreate): + try: + # Create the post + post = Post( + author=data.author, + title=data.title, + content=data.content + ) + db.add(post) + db.flush() + + # Create the thread + thread = Thread( + author=post.author, + title=post.title, + content=post.content + ) + db.add(thread) + db.flush() + + # Update the Post with thread_id + post.thread_id = thread.id + db.commit() + + return { + 'id': post.id, + 'author': post.author, + 'title': post.title, + 'content': post.content + } + except Exception as e: + db.rollback() + raise HTTPException(status_code=400, detail=str(e)) + + +@router.get('/{thread_id}', status_code=status.HTTP_200_OK) +async def get_thread_by_id(db: db_dependency, thread_id: int = Path(gt=0)): + posts = db.query(Post).filter(Post.thread_id == thread_id).all() + if posts: + return posts + + raise HTTPException(404, f'Could not find thread') + + +@router.post('/{thread_id}', status_code=status.HTTP_201_CREATED) +async def create_reply(db: db_dependency, data: PostCreate, thread_id: int = Path(gt=0)): + thread = db.query(Thread).filter(Thread.id == thread_id).first() + if thread: + post = Post( + thread_id=thread_id, + author=data.author, + title=data.title, + content=data.content + ) + db.add(post) + db.commit() + + return { + 'id': post.id, + 'author': post.author, + 'title': post.title, + 'content': post.content + } + + raise HTTPException(status_code=404, detail='Could not find thread') + + +@router.get('/catalog', status_code=status.HTTP_200_OK) +async def get_catalog(db: db_dependency): + return db.query(Thread).all() + -- 2.30.2 From 594c5c6de8c9c6927daca72c49447cf9289f64c1 Mon Sep 17 00:00:00 2001 From: agatha Date: Sat, 6 Apr 2024 14:50:59 -0400 Subject: [PATCH 4/9] chore: refactor --- backend/main.py | 2 +- backend/routers/forum.py | 28 +++++++++++++--------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/backend/main.py b/backend/main.py index a8de0cb..e392f81 100644 --- a/backend/main.py +++ b/backend/main.py @@ -8,5 +8,5 @@ app = FastAPI() models.Base.metadata.create_all(bind=engine) -app.include_router(auth.router) app.include_router(forum.router) +app.include_router(auth.router) diff --git a/backend/routers/forum.py b/backend/routers/forum.py index a7fda60..ee0a75b 100644 --- a/backend/routers/forum.py +++ b/backend/routers/forum.py @@ -27,11 +27,24 @@ class PostCreate(BaseModel): content: str = Field('') +@router.get('/catalog', status_code=status.HTTP_200_OK) +async def get_catalog(db: db_dependency): + return db.query(Thread).all() + + @router.get('/', status_code=status.HTTP_200_OK) async def get_posts(db: db_dependency): return db.query(Post).all() +@router.get('/{thread_id}', status_code=status.HTTP_200_OK) +async def get_thread_by_id(db: db_dependency, thread_id: int = Path(gt=0)): + posts = db.query(Post).filter(Post.thread_id == thread_id).all() + if posts: + return posts + + raise HTTPException(404, f'Could not find thread') + @router.post('/', status_code=status.HTTP_201_CREATED) async def create_thread(db: db_dependency, data: PostCreate): try: @@ -68,15 +81,6 @@ async def create_thread(db: db_dependency, data: PostCreate): raise HTTPException(status_code=400, detail=str(e)) -@router.get('/{thread_id}', status_code=status.HTTP_200_OK) -async def get_thread_by_id(db: db_dependency, thread_id: int = Path(gt=0)): - posts = db.query(Post).filter(Post.thread_id == thread_id).all() - if posts: - return posts - - raise HTTPException(404, f'Could not find thread') - - @router.post('/{thread_id}', status_code=status.HTTP_201_CREATED) async def create_reply(db: db_dependency, data: PostCreate, thread_id: int = Path(gt=0)): thread = db.query(Thread).filter(Thread.id == thread_id).first() @@ -98,9 +102,3 @@ async def create_reply(db: db_dependency, data: PostCreate, thread_id: int = Pat } raise HTTPException(status_code=404, detail='Could not find thread') - - -@router.get('/catalog', status_code=status.HTTP_200_OK) -async def get_catalog(db: db_dependency): - return db.query(Thread).all() - -- 2.30.2 From 8aa077441f4467beaf9531f6cae576c4dac1520e Mon Sep 17 00:00:00 2001 From: agatha Date: Sat, 6 Apr 2024 14:51:28 -0400 Subject: [PATCH 5/9] feat: add login and user creation endpoints --- backend/models.py | 14 +++++++-- backend/requirements.txt | 4 +++ backend/routers/auth.py | 65 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/backend/models.py b/backend/models.py index 0eaf353..fdd77b2 100644 --- a/backend/models.py +++ b/backend/models.py @@ -5,7 +5,7 @@ from sqlalchemy import Column, Integer, String, ForeignKey class Post(Base): __tablename__ = 'posts' - id = Column(Integer, primary_key=True) + id = Column(Integer, primary_key=True, index=True) thread_id = Column(Integer, ForeignKey("threads.id")) author = Column(String) title = Column(String) @@ -15,7 +15,17 @@ class Post(Base): class Thread(Base): __tablename__ = 'threads' - id = Column(Integer, primary_key=True) + id = Column(Integer, primary_key=True, index=True) author = Column(String) title = Column(String) content = Column(String) + + +class User(Base): + __tablename__ = 'users' + + id = Column(Integer, primary_key=True, index=True) + username = Column(String, unique=True) + email = Column(String, unique=True) + password = Column(String) + role = Column(String) diff --git a/backend/requirements.txt b/backend/requirements.txt index 09b688d..320f59f 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,3 +1,7 @@ fastapi uvicorn[standard] sqlalchemy +passlib[bcrypt] +pydantic +starlette +python-multipart \ No newline at end of file diff --git a/backend/routers/auth.py b/backend/routers/auth.py index 7980643..eec56ee 100644 --- a/backend/routers/auth.py +++ b/backend/routers/auth.py @@ -1,8 +1,65 @@ -from fastapi import APIRouter +from typing import Annotated +from fastapi import APIRouter, Depends +from pydantic import BaseModel +from passlib.context import CryptContext +from sqlalchemy.orm import Session +from starlette import status +from fastapi.security import OAuth2PasswordRequestForm + +from models import User +from database import SessionLocal router = APIRouter() +bcrypt_context = CryptContext(schemes=['bcrypt'], deprecated='auto') -@router.get('/auth/') -async def get_user(): - return {'user': 'authenticated'} + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() + + +db_dependency = Annotated[Session, Depends(get_db)] + + +def authenticate_user(username: str, password: str, db): + user = db.query(User).filter(User.username == username).first() + if not user: + return False + if not bcrypt_context.verify(password, user.password): + return False + return True + + +class CreateUser(BaseModel): + username: str + email: str + password: str + + +@router.post('/auth/create', status_code=status.HTTP_201_CREATED) +async def create_user(db: db_dependency, data: CreateUser): + create_user_model = User( + username=data.username, + email=data.email, + password=bcrypt_context.hash(data.password), + role='admin' + ) + + db.add(create_user_model) + db.commit() + + +@router.post('/auth/token', status_code=status.HTTP_200_OK) +async def get_token( + form_data: Annotated[OAuth2PasswordRequestForm, Depends()], + db: db_dependency +): + user = authenticate_user(form_data.username, form_data.password, db) + if user: + return "you good fam" + + return "failed authentication" -- 2.30.2 From 353218aca787789a71e819f4415f6ec88d436fab Mon Sep 17 00:00:00 2001 From: agatha Date: Sat, 6 Apr 2024 15:08:41 -0400 Subject: [PATCH 6/9] implement jwt --- backend/requirements.txt | 3 ++- backend/routers/auth.py | 29 ++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 320f59f..7fe8d81 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -4,4 +4,5 @@ sqlalchemy passlib[bcrypt] pydantic starlette -python-multipart \ No newline at end of file +python-multipart +python-jose[cryptography] \ No newline at end of file diff --git a/backend/routers/auth.py b/backend/routers/auth.py index eec56ee..a5f1081 100644 --- a/backend/routers/auth.py +++ b/backend/routers/auth.py @@ -1,3 +1,4 @@ +from datetime import timedelta, datetime from typing import Annotated from fastapi import APIRouter, Depends from pydantic import BaseModel @@ -5,12 +6,16 @@ from passlib.context import CryptContext from sqlalchemy.orm import Session from starlette import status from fastapi.security import OAuth2PasswordRequestForm +from jose import jwt from models import User from database import SessionLocal router = APIRouter() +SECRET_KEY = '3b004eeae34b43bd05226f210d9bdc2ad99abdd3c52bf32802906085b762ff55' +ALGORITHM = 'HS256' + bcrypt_context = CryptContext(schemes=['bcrypt'], deprecated='auto') @@ -31,7 +36,15 @@ def authenticate_user(username: str, password: str, db): return False if not bcrypt_context.verify(password, user.password): return False - return True + return user + + +def create_access_token(username: str, user_id: int, expires_delta: timedelta): + encode = {'sub': username, 'id': user_id} + expire = datetime.now() + expires_delta + encode.update({'exp': expire}) + + return jwt.encode(encode, SECRET_KEY, ALGORITHM) class CreateUser(BaseModel): @@ -40,6 +53,11 @@ class CreateUser(BaseModel): password: str +class Token(BaseModel): + access_token: str + token_type: str + + @router.post('/auth/create', status_code=status.HTTP_201_CREATED) async def create_user(db: db_dependency, data: CreateUser): create_user_model = User( @@ -53,13 +71,14 @@ async def create_user(db: db_dependency, data: CreateUser): db.commit() -@router.post('/auth/token', status_code=status.HTTP_200_OK) +@router.post('/auth/token', status_code=status.HTTP_200_OK, response_model=Token) async def get_token( form_data: Annotated[OAuth2PasswordRequestForm, Depends()], db: db_dependency ): user = authenticate_user(form_data.username, form_data.password, db) - if user: - return "you good fam" + if not user: + return "authentication failed" - return "failed authentication" + token = create_access_token(user.username, user.id, timedelta(minutes=20)) + return {'access_token': token, 'token_type': 'bearer'} -- 2.30.2 From ea4ae48b234a2daa644b3d73be2d8d5b9b71d485 Mon Sep 17 00:00:00 2001 From: agatha Date: Sat, 6 Apr 2024 15:29:08 -0400 Subject: [PATCH 7/9] add jwt validation --- backend/routers/auth.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/backend/routers/auth.py b/backend/routers/auth.py index a5f1081..9d1aed4 100644 --- a/backend/routers/auth.py +++ b/backend/routers/auth.py @@ -1,12 +1,12 @@ from datetime import timedelta, datetime from typing import Annotated -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from passlib.context import CryptContext from sqlalchemy.orm import Session from starlette import status -from fastapi.security import OAuth2PasswordRequestForm -from jose import jwt +from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer +from jose import jwt, JWTError from models import User from database import SessionLocal @@ -17,6 +17,7 @@ SECRET_KEY = '3b004eeae34b43bd05226f210d9bdc2ad99abdd3c52bf32802906085b762ff55' ALGORITHM = 'HS256' bcrypt_context = CryptContext(schemes=['bcrypt'], deprecated='auto') +oauth2_bearer = OAuth2PasswordBearer(tokenUrl='validate') def get_db(): @@ -58,6 +59,19 @@ class Token(BaseModel): token_type: str +async def get_current_user(token: Annotated[str, Depends(oauth2_bearer)]): + try: + payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) + username: str = payload.get('sub') + user_id: int = payload.get('id') + if username is None or user_id is None: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials") + + return {'username': username, 'user_id': user_id} + except JWTError: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials") + + @router.post('/auth/create', status_code=status.HTTP_201_CREATED) async def create_user(db: db_dependency, data: CreateUser): create_user_model = User( -- 2.30.2 From 24887274f0f5226a21aca3d56ad98b7d3b40a0df Mon Sep 17 00:00:00 2001 From: agatha Date: Sat, 6 Apr 2024 15:36:48 -0400 Subject: [PATCH 8/9] add router tags --- backend/routers/auth.py | 13 ++++++++----- backend/routers/forum.py | 4 +++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/backend/routers/auth.py b/backend/routers/auth.py index 9d1aed4..29ee23d 100644 --- a/backend/routers/auth.py +++ b/backend/routers/auth.py @@ -11,13 +11,16 @@ from jose import jwt, JWTError from models import User from database import SessionLocal -router = APIRouter() +router = APIRouter( + prefix='/auth', + tags=['auth'] +) SECRET_KEY = '3b004eeae34b43bd05226f210d9bdc2ad99abdd3c52bf32802906085b762ff55' ALGORITHM = 'HS256' bcrypt_context = CryptContext(schemes=['bcrypt'], deprecated='auto') -oauth2_bearer = OAuth2PasswordBearer(tokenUrl='validate') +oauth2_bearer = OAuth2PasswordBearer(tokenUrl='auth/token') def get_db(): @@ -72,7 +75,7 @@ async def get_current_user(token: Annotated[str, Depends(oauth2_bearer)]): raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials") -@router.post('/auth/create', status_code=status.HTTP_201_CREATED) +@router.post('/user/create', status_code=status.HTTP_201_CREATED) async def create_user(db: db_dependency, data: CreateUser): create_user_model = User( username=data.username, @@ -85,14 +88,14 @@ async def create_user(db: db_dependency, data: CreateUser): db.commit() -@router.post('/auth/token', status_code=status.HTTP_200_OK, response_model=Token) +@router.post('/token', status_code=status.HTTP_200_OK, response_model=Token) async def get_token( form_data: Annotated[OAuth2PasswordRequestForm, Depends()], db: db_dependency ): user = authenticate_user(form_data.username, form_data.password, db) if not user: - return "authentication failed" + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials") token = create_access_token(user.username, user.id, timedelta(minutes=20)) return {'access_token': token, 'token_type': 'bearer'} diff --git a/backend/routers/forum.py b/backend/routers/forum.py index ee0a75b..5558dbc 100644 --- a/backend/routers/forum.py +++ b/backend/routers/forum.py @@ -7,7 +7,9 @@ from starlette import status from database import SessionLocal from models import Post, Thread -router = APIRouter() +router = APIRouter( + tags=['forum'] +) def get_db(): -- 2.30.2 From 03a95c38723258d6adde0cb945b1c09fefa3f016 Mon Sep 17 00:00:00 2001 From: agatha Date: Sat, 6 Apr 2024 15:55:18 -0400 Subject: [PATCH 9/9] admins can create new users --- backend/routers/auth.py | 46 ++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/backend/routers/auth.py b/backend/routers/auth.py index 29ee23d..8bb11f9 100644 --- a/backend/routers/auth.py +++ b/backend/routers/auth.py @@ -1,4 +1,4 @@ -from datetime import timedelta, datetime +from datetime import timedelta, datetime, timezone from typing import Annotated from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel @@ -31,7 +31,22 @@ def get_db(): db.close() +async def get_current_user(token: Annotated[str, Depends(oauth2_bearer)]): + try: + payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) + username: str = payload.get('sub') + user_id: int = payload.get('id') + role: str = payload.get('role') + if username is None or user_id is None: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="1Could not validate credentials") + return {'username': username, 'user_id': user_id, 'role': role} + except JWTError as e: + print(str(e)) + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="2Could not validate credentials") + + db_dependency = Annotated[Session, Depends(get_db)] +user_dependency = Annotated[dict, Depends(get_current_user)] def authenticate_user(username: str, password: str, db): @@ -43,9 +58,9 @@ def authenticate_user(username: str, password: str, db): return user -def create_access_token(username: str, user_id: int, expires_delta: timedelta): - encode = {'sub': username, 'id': user_id} - expire = datetime.now() + expires_delta +def create_access_token(username: str, user_id: int, role: str, expires_delta: timedelta): + encode = {'sub': username, 'id': user_id, 'role': role} + expire = datetime.now(timezone.utc) + expires_delta encode.update({'exp': expire}) return jwt.encode(encode, SECRET_KEY, ALGORITHM) @@ -62,21 +77,14 @@ class Token(BaseModel): token_type: str -async def get_current_user(token: Annotated[str, Depends(oauth2_bearer)]): - try: - payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) - username: str = payload.get('sub') - user_id: int = payload.get('id') - if username is None or user_id is None: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials") - - return {'username': username, 'user_id': user_id} - except JWTError: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials") - - @router.post('/user/create', status_code=status.HTTP_201_CREATED) -async def create_user(db: db_dependency, data: CreateUser): +async def create_user(user: user_dependency, db: db_dependency, data: CreateUser): + if user is None: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Authentication failed') + + if user['role'] != 'admin': + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Not authorized') + create_user_model = User( username=data.username, email=data.email, @@ -97,5 +105,5 @@ async def get_token( if not user: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials") - token = create_access_token(user.username, user.id, timedelta(minutes=20)) + token = create_access_token(user.username, user.id, user.role, timedelta(minutes=20)) return {'access_token': token, 'token_type': 'bearer'} -- 2.30.2