Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions backend/app/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from datetime import datetime, timezone
from typing import Optional
from urllib.parse import urlencode

from fastapi import APIRouter, Depends, HTTPException, status, Header
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
Expand Down Expand Up @@ -94,22 +95,23 @@ async def get_github_authorize(state: Optional[str] = None):
)


@router.post("/github", response_model=GitHubOAuthResponse)
async def github_oauth_callback(request: GitHubOAuthRequest):
@router.get("/github/callback", response_model=GitHubOAuthResponse)
async def github_oauth_callback(code: str, state: Optional[str] = None):
"""
Complete GitHub OAuth flow.

GitHub redirects here after user authorizes.
Exchange the authorization code for JWT tokens.

Flow:
1. User is redirected from GitHub with a code
1. User is redirected from GitHub with a code and state
2. Exchange code for GitHub access token
3. Get user info from GitHub
4. Create/update user in database
5. Return JWT tokens
"""
try:
result = await auth_service.github_oauth_login(request.code)
result = await auth_service.github_oauth_login(code, state)
return result
except GitHubOAuthError as e:
raise HTTPException(
Expand Down
2 changes: 2 additions & 0 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from app.api.auth import router as auth_router
from app.api.contributors import router as contributors_router
from app.api.bounties import router as bounties_router
from app.api.notifications import router as notifications_router
Expand Down Expand Up @@ -46,6 +47,7 @@ async def lifespan(app: FastAPI):
allow_headers=["Content-Type", "Authorization"],
)

app.include_router(auth_router, prefix="/api", tags=["authentication"])
app.include_router(contributors_router)
app.include_router(bounties_router, prefix="/api", tags=["bounties"])
app.include_router(notifications_router, prefix="/api", tags=["notifications"])
Expand Down
68 changes: 67 additions & 1 deletion backend/app/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,70 @@ class UserResponse(BaseModel):
updated_at: datetime

class Config:
from_attributes = True
from_attributes = True


class GitHubOAuthRequest(BaseModel):
"""Request model for GitHub OAuth callback."""
code: str
state: Optional[str] = None


class GitHubOAuthResponse(BaseModel):
"""Response model for successful GitHub OAuth login."""
access_token: str
refresh_token: str
token_type: str = "bearer"
expires_in: int
user: UserResponse


class WalletAuthRequest(BaseModel):
"""Request model for wallet authentication."""
wallet_address: str
signature: str
message: str
nonce: Optional[str] = None


class WalletAuthResponse(BaseModel):
"""Response model for successful wallet authentication."""
access_token: str
refresh_token: str
token_type: str = "bearer"
expires_in: int
user: UserResponse


class LinkWalletRequest(BaseModel):
"""Request model for linking a wallet to user account."""
wallet_address: str
signature: str
message: str
nonce: Optional[str] = None


class LinkWalletResponse(BaseModel):
"""Response model for wallet linking."""
success: bool
message: str
user: UserResponse


class RefreshTokenRequest(BaseModel):
"""Request model for token refresh."""
refresh_token: str


class RefreshTokenResponse(BaseModel):
"""Response model for token refresh."""
access_token: str
token_type: str = "bearer"
expires_in: int


class AuthMessageResponse(BaseModel):
"""Response model for auth message generation."""
message: str
nonce: str
expires_at: datetime
4 changes: 3 additions & 1 deletion backend/app/services/auth_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,11 @@ def get_github_authorize_url(state: Optional[str] = None) -> tuple:
state = state or secrets.token_urlsafe(32)
_oauth_states[state] = {"created_at": datetime.now(timezone.utc),
"expires_at": datetime.now(timezone.utc) + timedelta(minutes=10)}
from urllib.parse import urlencode
params = {"client_id": GITHUB_CLIENT_ID, "redirect_uri": GITHUB_REDIRECT_URI,
"scope": "read:user user:email", "state": state, "response_type": "code"}
return f"https://github.com/login/oauth/authorize?{'&'.join(f'{k}={v}' for k,v in params.items())}", state
encoded_params = urlencode(params)
return f"https://github.com/login/oauth/authorize?{encoded_params}", state


def verify_oauth_state(state: str) -> bool:
Expand Down
Loading
Loading