feat: init new auth

This commit is contained in:
swve 2024-05-25 14:56:28 +02:00
parent f838a8512c
commit 1708b36818
34 changed files with 1853 additions and 3613 deletions

View file

@ -1,11 +1,14 @@
from datetime import timedelta
from typing import Literal, Optional
from fastapi import Depends, APIRouter, HTTPException, Response, status, Request
from fastapi.security import OAuth2PasswordRequestForm
from pydantic import BaseModel, EmailStr
from sqlmodel import Session
from src.db.users import UserRead
from src.db.users import AnonymousUser, PublicUser, UserRead
from src.core.events.database import get_db_session
from config.config import get_learnhouse_config
from src.security.auth import AuthJWT, authenticate_user
from src.security.auth import AuthJWT, authenticate_user, get_current_user
from src.services.auth.utils import get_google_user_info, signWithGoogle
router = APIRouter()
@ -74,6 +77,58 @@ async def login(
return result
class ThirdPartyLogin(BaseModel):
email: EmailStr
provider: Literal["google"]
access_token: str
@router.post("/oauth")
async def third_party_login(
request: Request,
response: Response,
body: ThirdPartyLogin,
org_id: Optional[int] = None,
current_user: AnonymousUser = Depends(get_current_user),
db_session: Session = Depends(get_db_session),
Authorize: AuthJWT = Depends(),
):
# Google
if body.provider == "google":
user = await signWithGoogle(
request, body.access_token, body.email, org_id, current_user, db_session
)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect Email or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token = Authorize.create_access_token(subject=user.email)
refresh_token = Authorize.create_refresh_token(subject=user.email)
Authorize.set_refresh_cookies(refresh_token)
# set cookies using fastapi
response.set_cookie(
key="access_token_cookie",
value=access_token,
httponly=False,
domain=get_learnhouse_config().hosting_config.cookie_config.domain,
expires=int(timedelta(hours=8).total_seconds()),
)
user = UserRead.model_validate(user)
result = {
"user": user,
"tokens": {"access_token": access_token, "refresh_token": refresh_token},
}
return result
@router.delete("/logout")
def logout(Authorize: AuthJWT = Depends()):
"""

View file

@ -0,0 +1,70 @@
import random
from typing import Optional
from fastapi import Depends, HTTPException, Request
import httpx
from sqlmodel import Session, select
from src.core.events.database import get_db_session
from src.db.users import User, UserCreate, UserRead
from src.security.auth import get_current_user
from src.services.users.users import create_user, create_user_without_org
async def get_google_user_info(access_token: str):
url = "https://www.googleapis.com/oauth2/v3/userinfo"
headers = {"Authorization": f"Bearer {access_token}"}
async with httpx.AsyncClient() as client:
response = await client.get(url, headers=headers)
if response.status_code != 200:
raise HTTPException(
status_code=response.status_code,
detail="Failed to fetch user info from Google",
)
return response.json()
async def signWithGoogle(
request: Request,
access_token: str,
email: str,
org_id: Optional[int] = None,
current_user=Depends(get_current_user),
db_session: Session = Depends(get_db_session),
):
# Google
google_user = await get_google_user_info(access_token)
user = db_session.exec(
select(User).where(User.email == google_user["email"])
).first()
if not user:
username = (
google_user["given_name"]
+ google_user["family_name"]
+ str(random.randint(10, 99))
)
user_object = UserCreate(
email=google_user["email"],
username=username,
password="",
first_name=google_user["given_name"],
last_name=google_user["family_name"],
avatar_image=google_user["picture"],
)
if org_id is not None:
user = await create_user(
request, db_session, current_user, user_object, org_id
)
return user
else:
user = await create_user_without_org(
request, db_session, current_user, user_object
)
return user
return UserRead.model_validate(user)

View file

@ -17,7 +17,7 @@ def send_account_creation_email(
<body>
<p>Hello {user.username}</p>
<p>Welcome to LearnHouse! , get started by creating your own organization or join a one.</p>
<p>Need some help to get started ? <a href="https://learn.learnhouse.io">LearnHouse Academy</a></p>
<p>Need some help to get started ? <a href="https://university.learnhouse.io">LearnHouse Academy</a></p>
</body>
</html>
""",