Skip to content

Commit 3bef5f2

Browse files
committed
feat(backend): integrate JWT authentication for repository endpoints
- Replace API key verification with JWT token authentication - Use get_current_user dependency for protected endpoints - Extract user_id from JWT tokens for multi-tenant support - Update GET /api/repos to require authenticated user - Update POST /api/repos to use JWT-based user identification Changes: - Import get_current_user from middleware.auth - Add Depends(get_current_user) to repository endpoints - Replace verify_api_key() calls with JWT verification - Use user_id from JWT for rate limiting and repo association - Generate user_id_hash from JWT user_id instead of API key Security improvements: - Proper user authentication per request - User-scoped repository access (foundation for RLS) - No hardcoded API keys in frontend - Session-based authentication flow Prepares backend for multi-tenant repository isolation
1 parent be96361 commit 3bef5f2

2 files changed

Lines changed: 26 additions & 21 deletions

File tree

backend/main.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
CodeIntel Backend API
33
FastAPI backend for codebase intelligence
44
"""
5-
from fastapi import FastAPI, HTTPException, Header, WebSocket, WebSocketDisconnect
5+
from fastapi import FastAPI, HTTPException, Header, WebSocket, WebSocketDisconnect, Depends
66
from fastapi.middleware.cors import CORSMiddleware
77
from pydantic import BaseModel
88
from typing import Optional, List
@@ -27,6 +27,7 @@
2727

2828
# Import routers
2929
from routes.auth import router as auth_router
30+
from middleware.auth import get_current_user
3031

3132
app = FastAPI(
3233
title="CodeIntel API",
@@ -141,21 +142,23 @@ async def health_check():
141142

142143

143144
@app.get("/api/repos")
144-
async def list_repositories(api_key: str = Header(None, alias="Authorization")):
145-
"""List all repositories"""
146-
verify_api_key(api_key)
145+
async def list_repositories(current_user: dict = Depends(get_current_user)):
146+
"""List all repositories for authenticated user"""
147+
user_id = current_user["user_id"]
147148

149+
# TODO: Filter repos by user_id once we add user_id column to repositories table
150+
# For now, return all repos (will fix in next section)
148151
repos = repo_manager.list_repos()
149152
return {"repositories": repos}
150153

151154

152155
@app.post("/api/repos")
153156
async def add_repository(
154157
request: AddRepoRequest,
155-
api_key: str = Header(None, alias="Authorization")
158+
current_user: dict = Depends(get_current_user)
156159
):
157160
"""Add a new repository with validation and cost controls"""
158-
key_data = verify_api_key(api_key)
161+
user_id = current_user["user_id"]
159162

160163
# Validate repository name
161164
valid_name, name_error = InputValidator.validate_repo_name(request.name)
@@ -168,10 +171,9 @@ async def add_repository(
168171
raise HTTPException(status_code=400, detail=f"Invalid Git URL: {url_error}")
169172

170173
# Check repository limit
171-
user_id = key_data.get("user_id")
172-
api_key_hash = hashlib.sha256(api_key.replace("Bearer ", "").encode()).hexdigest()
174+
user_id_hash = hashlib.sha256(user_id.encode()).hexdigest()
173175

174-
can_add, limit_error = cost_controller.check_repo_limit(user_id, api_key_hash)
176+
can_add, limit_error = cost_controller.check_repo_limit(user_id, user_id_hash)
175177
if not can_add:
176178
raise HTTPException(status_code=429, detail=limit_error)
177179

@@ -181,7 +183,7 @@ async def add_repository(
181183
git_url=request.git_url,
182184
branch=request.branch,
183185
user_id=user_id,
184-
api_key_hash=api_key_hash
186+
api_key_hash=user_id_hash
185187
)
186188

187189
# Check repo size before allowing indexing

frontend/src/components/Dashboard.tsx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useState, useEffect } from 'react'
22
import { Toaster } from '@/components/ui/sonner'
33
import { toast } from 'sonner'
4+
import { useAuth } from '../contexts/AuthContext'
45
import { RepoList } from './RepoList'
56
import { AddRepoForm } from './AddRepoForm'
67
import { SearchPanel } from './SearchPanel'
@@ -13,21 +14,23 @@ import { UserNav } from './UserNav'
1314
import type { Repository } from '../types'
1415

1516
const API_URL = 'http://localhost:8000'
16-
const API_KEY = 'dev-secret-key'
1717

1818
type RepoTab = 'overview' | 'search' | 'dependencies' | 'insights' | 'impact'
1919

2020
export function Dashboard() {
21+
const { session } = useAuth()
2122
const [repos, setRepos] = useState<Repository[]>([])
2223
const [selectedRepo, setSelectedRepo] = useState<string | null>(null)
2324
const [activeTab, setActiveTab] = useState<RepoTab>('overview')
2425
const [loading, setLoading] = useState(false)
2526
const [showPerformance, setShowPerformance] = useState(false)
2627

2728
const fetchRepos = async () => {
29+
if (!session?.access_token) return
30+
2831
try {
2932
const response = await fetch(`${API_URL}/api/repos`, {
30-
headers: { 'Authorization': `Bearer ${API_KEY}` }
33+
headers: { 'Authorization': `Bearer ${session.access_token}` }
3134
})
3235
const data = await response.json()
3336
setRepos(data.repositories || [])
@@ -50,7 +53,7 @@ export function Dashboard() {
5053
const response = await fetch(`${API_URL}/api/repos`, {
5154
method: 'POST',
5255
headers: {
53-
'Authorization': `Bearer ${API_KEY}`,
56+
'Authorization': `Bearer ${session.access_token}`,
5457
'Content-Type': 'application/json'
5558
},
5659
body: JSON.stringify({ name, git_url: gitUrl, branch })
@@ -60,7 +63,7 @@ export function Dashboard() {
6063

6164
await fetch(`${API_URL}/api/repos/${data.repo_id}/index`, {
6265
method: 'POST',
63-
headers: { 'Authorization': `Bearer ${API_KEY}` }
66+
headers: { 'Authorization': `Bearer ${session.access_token}` }
6467
})
6568

6669
await fetchRepos()
@@ -84,7 +87,7 @@ export function Dashboard() {
8487
setLoading(true)
8588
await fetch(`${API_URL}/api/repos/${selectedRepo}/index`, {
8689
method: 'POST',
87-
headers: { 'Authorization': `Bearer ${API_KEY}` }
90+
headers: { 'Authorization': `Bearer ${session.access_token}` }
8891
})
8992
await fetchRepos()
9093
// RepoOverview component will show the toast and progress
@@ -143,7 +146,7 @@ export function Dashboard() {
143146
{/* Performance Dashboard Overlay */}
144147
{showPerformance && (
145148
<div className="mb-6">
146-
<PerformanceDashboard apiUrl={API_URL} apiKey={API_KEY} />
149+
<PerformanceDashboard apiUrl={API_URL} apiKey={session.access_token} />
147150
</div>
148151
)}
149152

@@ -254,24 +257,24 @@ export function Dashboard() {
254257
repo={selectedRepoData}
255258
onReindex={handleReindex}
256259
apiUrl={API_URL}
257-
apiKey={API_KEY}
260+
apiKey={session.access_token}
258261
/>
259262
)}
260263

261264
{activeTab === 'search' && (
262-
<SearchPanel repoId={selectedRepo} apiUrl={API_URL} apiKey={API_KEY} />
265+
<SearchPanel repoId={selectedRepo} apiUrl={API_URL} apiKey={session.access_token} />
263266
)}
264267

265268
{activeTab === 'dependencies' && (
266-
<DependencyGraph repoId={selectedRepo} apiUrl={API_URL} apiKey={API_KEY} />
269+
<DependencyGraph repoId={selectedRepo} apiUrl={API_URL} apiKey={session.access_token} />
267270
)}
268271

269272
{activeTab === 'insights' && (
270-
<StyleInsights repoId={selectedRepo} apiUrl={API_URL} apiKey={API_KEY} />
273+
<StyleInsights repoId={selectedRepo} apiUrl={API_URL} apiKey={session.access_token} />
271274
)}
272275

273276
{activeTab === 'impact' && (
274-
<ImpactAnalyzer repoId={selectedRepo} apiUrl={API_URL} apiKey={API_KEY} />
277+
<ImpactAnalyzer repoId={selectedRepo} apiUrl={API_URL} apiKey={session.access_token} />
275278
)}
276279
</div>
277280
)}

0 commit comments

Comments
 (0)