Skip to content

Commit

Permalink
fix: authorization (#253)
Browse files Browse the repository at this point in the history
- 重构登录逻辑 
- close #249
  • Loading branch information
xingwanying authored Aug 22, 2024
2 parents 404c041 + 6f1ce2f commit 83e815a
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 69 deletions.
25 changes: 12 additions & 13 deletions server/auth/get_user_info.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from typing import Annotated
from fastapi import Cookie
from fastapi import Request
import httpx
import secrets
import random
Expand Down Expand Up @@ -49,11 +48,12 @@ async def generateAnonymousUser(clientId: str):
seed = clientId[:4]
random_name = f"{seed}_{random_str(4)}"
data = {
"anonymous": True,
"id": token,
"sub": token,
"nickname": random_name,
"name": random_name,
"picture": f"https://picsum.photos/seed/{seed}/100/100",
"sub": seed,
"sid": secrets.token_urlsafe(32)
}

Expand All @@ -64,25 +64,24 @@ async def getAnonymousUserInfoByToken(token: str):
rows = supabase.table("profiles").select("*").eq("id", token).execute()
return rows.data[0] if (len(rows.data) > 0) else None

async def get_user_id(petercat_user_token: Annotated[str | None, Cookie()] = None):
async def get_user_id(request: Request):
user_info = request.session.get('user')
try:
if petercat_user_token is None:
if user_info is None:
return None
user_info = await getUserInfoByToken(petercat_user_token)
return user_info['id']
return user_info['sub']

except Exception:
return None

async def get_user_access_token(petercat_user_token: Annotated[str | None, Cookie()] = None):
async def get_user_access_token(request: Request):
try:
if petercat_user_token is None:
return None
user_info = await getUserInfoByToken(petercat_user_token)
user_info = request.session.get('user')
if user_info is None:
return None
access_token = await getUserAccessToken(user_id=user_info['id'])
print(f"get_user_access_token: user_info={user_info}, access_token={access_token}")

access_token = await getUserAccessToken(user_id=user_info['sub'])
return access_token
except Exception:
return None

1 change: 1 addition & 0 deletions server/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ python-multipart
httpx[socks]
load_dotenv
supabase
authlib==0.14.3
boto3>=1.34.84
PyJWT
pydantic>=2.7.0
Expand Down
106 changes: 50 additions & 56 deletions server/routers/auth.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from typing import Annotated
from fastapi import APIRouter, Cookie, Request, HTTPException, status, Response
from fastapi import APIRouter, Request, HTTPException, status
from fastapi.responses import RedirectResponse
import httpx
import secrets
from petercat_utils import get_client, get_env_variable
from starlette.config import Config
from authlib.integrations.starlette_client import OAuth

from auth.get_user_info import generateAnonymousUser, getAnonymousUserInfoByToken, getUserInfoByToken
from auth.get_user_info import generateAnonymousUser

AUTH0_DOMAIN = get_env_variable("AUTH0_DOMAIN")

Expand All @@ -18,77 +19,70 @@

WEB_URL = get_env_variable("WEB_URL")

config = Config(environ={
"AUTH0_CLIENT_ID": CLIENT_ID,
"AUTH0_CLIENT_SECRET": CLIENT_SECRET,
})

oauth = OAuth(config)
oauth.register(
name="auth0",
server_metadata_url=f'https://{AUTH0_DOMAIN}/.well-known/openid-configuration',
client_kwargs={
'scope': 'openid email profile'
}
)

router = APIRouter(
prefix="/api/auth",
tags=["auth"],
responses={404: {"description": "Not found"}},
)

async def getTokenByCode(code):
token_url = f"https://{AUTH0_DOMAIN}/oauth/token"
headers = {"content-type": "application/x-www-form-urlencoded"}
data = {
"grant_type": "authorization_code",
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"code": code,
"redirect_uri": CALLBACK_URL,
}
async with httpx.AsyncClient() as client:
response = await client.post(token_url, headers=headers, data=data)
token_response = response.json()
print(f"token_response={token_response}")

if "access_token" not in token_response:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Failed to get access token")
return token_response['access_token']

async def getAnonymousUser(request: Request, response: Response):
async def getAnonymousUser(request: Request):
clientId = request.query_params.get("clientId")
if not clientId:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Missing clientId")
token, data = await generateAnonymousUser(clientId)

supabase = get_client()
supabase.table("profiles").upsert(data).execute()
response.set_cookie(key="petercat_user_token", value=token, httponly=True, secure=True, samesite='Lax')
return { "data": data, "status": 200}
request.session['user'] = data
return data

@router.get("/login")
def login():
redirect_uri = f"https://{AUTH0_DOMAIN}/authorize?audience={API_AUDIENCE}&response_type=code&client_id={CLIENT_ID}&redirect_uri={CALLBACK_URL}&scope=openid%20profile%20email%20read%3Ausers%20read%3Auser_idp_tokens&state=STATE"
return RedirectResponse(redirect_uri)
async def login(request: Request):
return await oauth.auth0.authorize_redirect(request, redirect_uri=CALLBACK_URL)

@router.get('/logout')
async def logout(request: Request):
request.session.pop('user', None)
return RedirectResponse(url='/')

@router.get("/callback")
async def callback(request: Request, response: Response):
async def callback(request: Request):
print(f"auth_callback: {request.query_params}")
code = request.query_params.get("code")
if not code:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Missing authorization code")
token = await getTokenByCode(code)
if not token:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Missing authorization token")
data = await getUserInfoByToken(token)
if data is None:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Missing authorization token")
supabase = get_client()
supabase.table("profiles").upsert(data).execute()
print(f"auth_callback: {data}, token={token}")
response = RedirectResponse(url=f'{WEB_URL}', status_code=302)
response.set_cookie(key="petercat_user_token", value=token, httponly=True, secure=False, samesite='Lax')

return response
auth0_token = await oauth.auth0.authorize_access_token(request)
user_info = auth0_token.get('userinfo')
if user_info:
request.session['user'] = dict(user_info)
data = {
"id": user_info["sub"],
"nickname": user_info.get("nickname"),
"name": user_info.get("name"),
"picture": user_info.get("picture"),
"sub": user_info["sub"],
"sid": secrets.token_urlsafe(32)
}
supabase = get_client()
supabase.table("profiles").upsert(data).execute()
return RedirectResponse(url=f'{WEB_URL}', status_code=302)

@router.get("/userinfo")
async def userinfo(request: Request, response: Response, petercat_user_token: Annotated[str | None, Cookie()] = None):
print(f"petercat_user_token: {petercat_user_token}")
if not petercat_user_token:
return await getAnonymousUser(request, response)
data = await getAnonymousUserInfoByToken(petercat_user_token) if petercat_user_token.startswith("client|") else await getUserInfoByToken(petercat_user_token)
if data is None:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Failed to get access token")
if data :
async def userinfo(request: Request):
user = request.session.get('user')

if not user:
data = await getAnonymousUser(request)
return { "data": data, "status": 200}
else:
return RedirectResponse(url=LOGIN_URL, status_code=303)
return { "data": user, "status": 200}

0 comments on commit 83e815a

Please sign in to comment.