login with JWT

This commit is contained in:
2026-05-15 22:58:53 +08:00
parent d14fe10ea7
commit 1c513a2f3e
5 changed files with 150 additions and 6 deletions
+74 -6
View File
@@ -1,8 +1,12 @@
import httpx import httpx
from fastapi import APIRouter, Request, HTTPException from fastapi import APIRouter, Depends, Request, HTTPException
from fastapi.responses import RedirectResponse from fastapi.responses import RedirectResponse
import os import os
import secrets import secrets
from auth.deps import get_current_user
from db import SessionLocal
from models import User
from auth.jwt import create_access_token, create_refresh_token, decode_token
router = APIRouter() router = APIRouter()
@@ -28,7 +32,13 @@ def login_google():
) )
response = RedirectResponse(url) response = RedirectResponse(url)
response.set_cookie("oauth_state", state, httponly=True) response.set_cookie(
"oauth_state",
state,
httponly=True,
secure=True,
samesite="lax"
)
return response return response
@router.get("/auth/callback") @router.get("/auth/callback")
@@ -57,10 +67,68 @@ async def auth_callback(request: Request, code: str, state: str):
GOOGLE_USERINFO_URL, GOOGLE_USERINFO_URL,
headers={"Authorization": f"Bearer {access_token}"}, headers={"Authorization": f"Bearer {access_token}"},
) )
user_info = user_res.json() user_info = user_res.json()
db = SessionLocal()
user = db.query(User).filter(User.email == user_info["email"]).first()
if not user:
user = User(
google_id=user_info["id"],
email=user_info["email"],
name=user_info["name"],
avatar=user_info["picture"],
)
db.add(user)
db.commit()
db.refresh(user)
db.close()
jwt_token = create_access_token({
"sub": user.id,
"email": user.email,
})
refresh_token = create_refresh_token({
"sub": user.id,
"email": user.email,
})
return { return {
"email": user_info["email"], "access_token": jwt_token,
"name": user_info["name"], "refresh_token": refresh_token,
"picture": user_info["picture"], "token_type": "bearer",
} "user": {
"email": user.email,
"name": user.name,
"avatar": user.avatar,
}
}
@router.get("/me")
def me(user = Depends(get_current_user)):
return {
"email": user.email,
"name": user.name,
"avatar": user.avatar,
}
@router.post("/refresh")
def refresh_token(refresh_token: str):
payload = decode_token(refresh_token)
if payload.get("type") != "refresh":
raise HTTPException(status_code=401)
user_id = payload.get("sub")
new_access = create_access_token({"sub": user_id})
return {"access_token": new_access}
@router.post("/logout")
def logout():
return {"message": "ok"}
View File
+19
View File
@@ -0,0 +1,19 @@
from fastapi import Request, HTTPException
import jwt
import os
JWT_SECRET = os.getenv("JWT_SECRET")
JWT_ALGORITHM = os.getenv("JWT_ALGORITHM", "HS256")
def get_current_user(request: Request):
token = request.cookies.get("access_token")
if not token:
raise HTTPException(status_code=401, detail="Not authenticated")
try:
payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
return payload["user_id"]
except:
raise HTTPException(status_code=401, detail="Invalid token")
+53
View File
@@ -0,0 +1,53 @@
import jwt
from datetime import datetime, timedelta, timezone
import os
from fastapi import HTTPException, status
JWT_SECRET = os.getenv("JWT_SECRET")
JWT_ALGORITHM = os.getenv("JWT_ALGORITHM", "HS256")
def now():
return datetime.now(timezone.utc)
def create_access_token(data: dict):
payload = {
**data,
"type": "access",
"exp": now() + timedelta(minutes=60),
"iat": now(),
}
return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)
def create_refresh_token(data: dict):
payload = {
**data,
"type": "refresh",
"exp": now() + timedelta(days=7),
"iat": now(),
}
return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)
def decode_token(token: str):
try:
payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
return payload
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Token expired")
except jwt.InvalidTokenError:
raise HTTPException(status_code=401, detail="Invalid token")
def verify_access_token(token: str):
payload = decode_token(token)
if payload.get("type") != "access":
raise HTTPException(status_code=401, detail="Invalid access token")
return payload
def verify_refresh_token(token: str):
payload = decode_token(token)
if payload.get("type") != "refresh":
raise HTTPException(status_code=401, detail="Invalid refresh token")
return payload
+4
View File
@@ -1,5 +1,9 @@
from fastapi import FastAPI from fastapi import FastAPI
from auth import router as auth_router from auth import router as auth_router
from models import Base
from db import engine
Base.metadata.create_all(bind=engine)
app = FastAPI() app = FastAPI()