Skip to content

Commit 1293ee8

Browse files
committed
Feat: start work on the auth flow
1 parent 768e71f commit 1293ee8

4 files changed

Lines changed: 80 additions & 7 deletions

File tree

src/discord-cluster-manager/api/main.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import asyncio
2+
import base64
3+
import os
24
import time
35
from dataclasses import asdict
46

7+
import requests
58
from cogs.submit_cog import SubmitCog
69
from consts import _GPU_LOOKUP, SubmissionMode, get_gpu_by_name
710
from discord import app_commands
11+
from env import CLI_DISCORD_CLIENT_ID, CLI_DISCORD_CLIENT_SECRET, CLI_TOKEN_URL
812
from fastapi import FastAPI, HTTPException, UploadFile
913
from utils import LeaderboardItem, build_task_config
1014

@@ -53,6 +57,75 @@ async def update(self, message: str):
5357
pass
5458

5559

60+
@app.get("/auth/cli")
61+
async def cli_auth(code: str, state: str = None):
62+
"""
63+
Handle Discord OAuth redirect. This endpoint receives the authorization code
64+
and state parameter from Discord's OAuth flow.
65+
66+
Args:
67+
code (str): Authorization code from Discord OAuth
68+
state (str): Base64 encoded client ID from CLI
69+
"""
70+
71+
if not code or not state:
72+
raise HTTPException(status_code=400, detail="Missing authorization code or state")
73+
74+
client_id = CLI_DISCORD_CLIENT_ID
75+
client_secret = CLI_DISCORD_CLIENT_SECRET
76+
redirect_uri = os.environ.get("HEROKU_APP_DEFAULT_DOMAIN_NAME") or os.getenv("POPCORN_API_URL")
77+
token_url = CLI_TOKEN_URL
78+
79+
if not client_id or not client_secret:
80+
raise HTTPException(status_code=500, detail="Discord client ID or secret not configured.")
81+
82+
if not token_url:
83+
raise HTTPException(status_code=500, detail="Discord token URL not configured.")
84+
85+
if not redirect_uri:
86+
raise HTTPException(
87+
status_code=500,
88+
detail="Redirect URI not configured. "
89+
"If running locally, set env variable `POPCORN_API_URL` to your local API URL.",
90+
)
91+
92+
token_data = {
93+
"client_id": client_id,
94+
"client_secret": client_secret,
95+
"grant_type": "authorization_code",
96+
"code": code,
97+
"redirect_uri": redirect_uri + "/auth/cli",
98+
}
99+
100+
token_response = requests.post(token_url, data=token_data)
101+
if token_response.status_code != 200:
102+
raise HTTPException(
103+
status_code=401, detail=f"Failed to authenticate with Discord: {token_response.text}"
104+
)
105+
106+
token_json = token_response.json()
107+
access_token = token_json.get("access_token")
108+
109+
user_url = "https://discord.com/api/users/@me"
110+
headers = {"Authorization": f"Bearer {access_token}"}
111+
112+
user_response = requests.get(user_url, headers=headers)
113+
if user_response.status_code != 200:
114+
raise HTTPException(status_code=401, detail="Failed to retrieve user information")
115+
116+
user_json = user_response.json()
117+
user_id = user_json.get("id")
118+
119+
cli_id = ""
120+
if state:
121+
try:
122+
cli_id = base64.b64decode(state).decode("utf-8")
123+
except Exception as e:
124+
raise HTTPException(status_code=400, detail=f"Invalid state parameter: {str(e)}") from e
125+
126+
return {"status": "success", "user_id": user_id, "cli_id": cli_id}
127+
128+
56129
@app.post("/{leaderboard_name}/{gpu_type}/{submission_mode}")
57130
async def run_submission(
58131
leaderboard_name: str, gpu_type: str, submission_mode: str, file: UploadFile

src/discord-cluster-manager/env.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ def init_environment():
2121
DISCORD_CLUSTER_STAGING_ID = os.getenv("DISCORD_CLUSTER_STAGING_ID")
2222
DISCORD_DEBUG_CLUSTER_STAGING_ID = os.getenv("DISCORD_DEBUG_CLUSTER_STAGING_ID")
2323

24+
# Only required to run the CLI against this instance
25+
# setting theses is required only to run the CLI against local instance
26+
CLI_DISCORD_CLIENT_ID = os.getenv("CLI_DISCORD_CLIENT_ID", "")
27+
CLI_DISCORD_CLIENT_SECRET = os.getenv("CLI_DISCORD_CLIENT_SECRET", "")
28+
CLI_TOKEN_URL = os.getenv("CLI_TOKEN_URL", "")
29+
2430
# GitHub-specific constants
2531
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
2632
GITHUB_REPO = os.getenv("GITHUB_REPO")

src/discord-cluster-manager/migrations/20241226_01_ZQSOK-add_gpu_type_to_submission.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,14 @@
55

66
from yoyo import step
77

8-
__depends__ = {'20241224_01_Pg4FX-delete-cascade'}
8+
__depends__ = {"20241224_01_Pg4FX-delete-cascade"}
99

1010
steps = [
1111
step("DROP TABLE leaderboard.runinfo"),
12-
1312
step("""
1413
ALTER TABLE leaderboard.submission
1514
ADD COLUMN gpu_type TEXT NOT NULL DEFAULT 'nvidia'
1615
"""),
17-
1816
step("ALTER TABLE leaderboard.submission ADD COLUMN stdout TEXT"),
19-
2017
step("ALTER TABLE leaderboard.submission ADD COLUMN profiler_output TEXT"),
2118
]

src/discord-cluster-manager/migrations/20250221_01_GA8ro-submission-collection.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
step("DROP TABLE IF EXISTS leaderboard.submission;"),
1313
step("DROP TABLE IF EXISTS leaderboard.code_files;"),
1414
step("DROP TABLE IF EXISTS leaderboard.runs;"),
15-
1615
# create three new tables: One for deduplicating submitted code files,
1716
# one for the submission itself, and one for individual runs
1817
# The submission itself contains the code and the targeted leaderboard
@@ -25,7 +24,6 @@
2524
hash TEXT GENERATED ALWAYS AS (encode(sha256(code::bytea), 'hex')) STORED
2625
)
2726
"""),
28-
2927
step("""
3028
CREATE TABLE IF NOT EXISTS leaderboard.submission (
3129
id SERIAL PRIMARY KEY,
@@ -37,7 +35,6 @@
3735
done BOOLEAN DEFAULT FALSE
3836
)
3937
"""),
40-
4138
# the runs themselves contain information about a particular execution of that code.
4239
# This includes start and end time
4340
# Note that `score` can be NULL for non-ranked submissions

0 commit comments

Comments
 (0)