first commit
This commit is contained in:
34
app/core/config.py
Normal file
34
app/core/config.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from __future__ import annotations
|
||||
from pydantic_settings import BaseSettings
|
||||
from pydantic import Field
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
model_config = {
|
||||
"env_file": ".env",
|
||||
"extra": "allow",
|
||||
}
|
||||
|
||||
# Read DATABASE_URL from environment via model_config; avoid using Field(..., env=...)
|
||||
DATABASE_URL: str = "sqlite:///./dev.db"
|
||||
JWT_SECRET: str
|
||||
ALGORITHM: str = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 15
|
||||
REFRESH_TOKEN_EXPIRE_DAYS: int = 30
|
||||
|
||||
GOOGLE_CLIENT_ID: str | None = None
|
||||
GOOGLE_CLIENT_SECRET: str | None = None
|
||||
GOOGLE_REDIRECT_URL: str | None = None
|
||||
|
||||
GITHUB_CLIENT_ID: str | None = None
|
||||
GITHUB_CLIENT_SECRET: str | None = None
|
||||
GITHUB_REDIRECT_URL: str | None = None
|
||||
|
||||
SERVER_NAME: str = "localhost:8000"
|
||||
DEBUG: bool = True
|
||||
|
||||
|
||||
settings = Settings()
|
||||
27
app/core/oauth.py
Normal file
27
app/core/oauth.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from urllib.parse import urlencode
|
||||
from typing import Dict
|
||||
|
||||
from app.core.config import settings
|
||||
|
||||
|
||||
def google_authorize_url(state: str = "state") -> str:
|
||||
params = {
|
||||
"client_id": settings.GOOGLE_CLIENT_ID,
|
||||
"redirect_uri": settings.GOOGLE_REDIRECT_URL,
|
||||
"response_type": "code",
|
||||
"scope": "openid email profile",
|
||||
"state": state,
|
||||
"access_type": "offline",
|
||||
"prompt": "consent",
|
||||
}
|
||||
return "https://accounts.google.com/o/oauth2/v2/auth?" + urlencode(params)
|
||||
|
||||
|
||||
def github_authorize_url(state: str = "state") -> str:
|
||||
params = {
|
||||
"client_id": settings.GITHUB_CLIENT_ID,
|
||||
"redirect_uri": settings.GITHUB_REDIRECT_URL,
|
||||
"scope": "user:email",
|
||||
"state": state,
|
||||
}
|
||||
return "https://github.com/login/oauth/authorize?" + urlencode(params)
|
||||
38
app/core/security.py
Normal file
38
app/core/security.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Tuple
|
||||
|
||||
import jwt
|
||||
from argon2 import PasswordHasher
|
||||
|
||||
from app.core.config import settings
|
||||
|
||||
ph = PasswordHasher()
|
||||
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
return ph.hash(password)
|
||||
|
||||
|
||||
def verify_password(hash: str, password: str) -> bool:
|
||||
try:
|
||||
return ph.verify(hash, password)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def create_access_token(sub: str) -> Tuple[str, datetime]:
|
||||
expire = datetime.now(timezone.utc) + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
payload = {"sub": sub, "exp": expire}
|
||||
token = jwt.encode(payload, settings.JWT_SECRET, algorithm=settings.ALGORITHM)
|
||||
return token, expire
|
||||
|
||||
|
||||
def create_refresh_token(sub: str) -> Tuple[str, datetime]:
|
||||
expire = datetime.now(timezone.utc) + timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS)
|
||||
payload = {"sub": sub, "exp": expire}
|
||||
token = jwt.encode(payload, settings.JWT_SECRET, algorithm=settings.ALGORITHM)
|
||||
return token, expire
|
||||
|
||||
|
||||
def decode_token(token: str) -> dict:
|
||||
return jwt.decode(token, settings.JWT_SECRET, algorithms=[settings.ALGORITHM])
|
||||
Reference in New Issue
Block a user