forked from AOSSIE-Org/Devr.AI
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathauth.py
More file actions
284 lines (262 loc) · 11 KB
/
auth.py
File metadata and controls
284 lines (262 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
from fastapi import APIRouter, Request, HTTPException, Query
from fastapi.responses import HTMLResponse
from app.database.supabase.client import get_supabase_client
from app.services.auth.verification import find_user_by_session_and_verify, get_verification_session_info
from app.services.github.user.profiling import profile_user_from_github
from typing import Optional
import logging
import asyncio
logger = logging.getLogger(__name__)
router = APIRouter()
@router.get("/callback", response_class=HTMLResponse)
async def auth_callback(request: Request, code: Optional[str] = Query(None), session: Optional[str] = Query(None)):
"""
Handles the OAuth callback from Supabase after a user authorizes on GitHub.
"""
logger.info(
f"OAuth callback received with code: {'[PRESENT]' if code else '[MISSING]'}, session: {'[PRESENT]' if session else '[MISSING]'}")
if not code:
logger.error("Missing authorization code in callback")
return _error_response("Missing authorization code. Please try the verification process again.")
if not session:
logger.error("Missing session ID in callback")
return _error_response("Missing session ID. Please try the !verify_github command again.")
# Check if session is valid and not expired
session_info = await get_verification_session_info(session)
if not session_info:
logger.error(f"Invalid or expired session ID: {session}")
return _error_response("Your verification session has expired. Please run the !verify_github command again.")
supabase = get_supabase_client()
try:
# Exchange code for session
logger.info("Exchanging authorization code for session")
session_response = await supabase.auth.exchange_code_for_session({
"auth_code": code,
})
if not session_response or not session_response.user:
logger.error("Failed to exchange code for session")
return _error_response("Authentication failed. Could not retrieve user session.")
user = session_response.user
logger.info(f"Successfully got user session for user: {user.id}")
# Extract GitHub info from user metadata
github_id = user.user_metadata.get("provider_id")
github_username = user.user_metadata.get("user_name")
email = user.email
if not github_id or not github_username:
logger.error(f"Missing GitHub details - ID: {github_id}, Username: {github_username}")
return _error_response("Could not retrieve GitHub details from user session.")
# Verify user using session ID
logger.info(f"Verifying user with session ID: {session}")
verified_user = await find_user_by_session_and_verify(
session_id=session,
github_id=str(github_id),
github_username=github_username,
email=email
)
if not verified_user:
logger.error("User verification failed - no pending verification found")
return _error_response("No pending verification found or verification has expired. Please try the !verify_github command again.")
logger.info(f"Successfully verified user: {verified_user.id}!")
logger.info(f"Indexing user: {verified_user.id} into Weaviate...")
try:
asyncio.create_task(profile_user_from_github(str(verified_user.id), github_username))
logger.info(f"User profiling started in background for: {verified_user.id}")
except Exception as e:
logger.error(f"Error starting user profiling: {verified_user.id}: {str(e)}")
return _success_response(github_username)
except Exception as e:
logger.error(f"Unexpected error in OAuth callback: {str(e)}", exc_info=True)
# Handle specific error cases
if "already linked" in str(e):
return _error_response(f"Error: {str(e)}")
return _error_response("An unexpected error occurred during verification. Please try again.")
@router.get("/session/{session_id}")
async def get_session_status(session_id: str):
"""Get the status of a verification session"""
session_info = await get_verification_session_info(session_id)
if not session_info:
raise HTTPException(status_code=404, detail="Session not found or expired")
return {
"valid": True,
"discord_id": session_info["discord_id"],
"expiry_time": session_info["expiry_time"],
"time_remaining": session_info["time_remaining"]
}
def _success_response(github_username: str) -> str:
"""Generate success HTML response"""
return f"""
<!DOCTYPE html>
<html>
<head>
<title>Verification Successful!</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
margin: 0;
padding: 20px;
box-sizing: border-box;
}}
.container {{
text-align: center;
padding: 40px;
background: white;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
max-width: 500px;
width: 100%;
}}
h1 {{
color: #28a745;
margin-bottom: 20px;
font-size: 2rem;
}}
.github-info {{
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
border-left: 4px solid #28a745;
}}
code {{
background: #e9ecef;
padding: 4px 8px;
border-radius: 4px;
font-family: 'Monaco', 'Consolas', monospace;
color: #495057;
font-weight: bold;
}}
.close-btn {{
margin-top: 20px;
padding: 12px 24px;
background: #007bff;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
}}
.close-btn:hover {{
background: #0056b3;
}}
.success-icon {{
font-size: 4rem;
margin-bottom: 20px;
}}
.auto-close {{
margin-top: 15px;
color: #6c757d;
font-size: 0.9rem;
}}
</style>
</head>
<body>
<div class="container">
<div class="success-icon">✅</div>
<h1>Verification Successful!</h1>
<div class="github-info">
<p><strong>Your Discord account has been successfully linked!</strong></p>
<p>GitHub User: <code>{github_username}</code></p>
</div>
<p>You can now access all features that require GitHub authentication.</p>
<button class="close-btn" onclick="window.close()">Close Window</button>
<p class="auto-close">This window will close automatically in 5 seconds.</p>
</div>
<script>
// Auto-close after 5 seconds
setTimeout(() => {{
window.close();
}}, 5000);
</script>
</body>
</html>
"""
def _error_response(error_message: str) -> str:
"""Generate error HTML response"""
return f"""
<!DOCTYPE html>
<html>
<head>
<title>Verification Failed</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%);
margin: 0;
padding: 20px;
box-sizing: border-box;
}}
.container {{
text-align: center;
padding: 40px;
background: white;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
max-width: 500px;
width: 100%;
}}
h1 {{
color: #dc3545;
margin-bottom: 20px;
font-size: 2rem;
}}
.error-message {{
background: #f8d7da;
color: #721c24;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
border-left: 4px solid #dc3545;
}}
.close-btn {{
margin-top: 20px;
padding: 12px 24px;
background: #dc3545;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
}}
.close-btn:hover {{
background: #c82333;
}}
.error-icon {{
font-size: 4rem;
margin-bottom: 20px;
}}
.help-text {{
color: #6c757d;
font-size: 0.9rem;
margin-top: 15px;
}}
</style>
</head>
<body>
<div class="container">
<div class="error-icon">❌</div>
<h1>Verification Failed</h1>
<div class="error-message">
<p>{error_message}</p>
</div>
<p>Please return to Discord and try the <code>!verify_github</code> command again.</p>
<button class="close-btn" onclick="window.close()">Close Window</button>
<p class="help-text">If you continue to experience issues, please contact support.</p>
</div>
</body>
</html>
"""