Skip to content

Commit 3724732

Browse files
feat(auth): add CLI auth endpoints (start, poll, approve)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-Claude) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
1 parent 389e8eb commit 3724732

File tree

3 files changed

+135
-0
lines changed

3 files changed

+135
-0
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { json } from '@sveltejs/kit';
2+
import type { RequestHandler } from './$types';
3+
import { getCurrentUser, generateId } from '$lib/server/auth';
4+
5+
export const POST: RequestHandler = async ({ platform, cookies, request }) => {
6+
const env = platform?.env;
7+
if (!env) return json({ error: 'Platform env not available' }, { status: 500 });
8+
9+
const user = await getCurrentUser(request, cookies, env.DB, env.JWT_SECRET);
10+
if (!user) return json({ error: 'Unauthorized' }, { status: 401 });
11+
12+
let body;
13+
try {
14+
body = await request.json();
15+
} catch {
16+
return json({ error: 'Invalid request body' }, { status: 400 });
17+
}
18+
19+
const { code } = body;
20+
if (!code || typeof code !== 'string') {
21+
return json({ error: 'Code is required' }, { status: 400 });
22+
}
23+
24+
const row = await env.DB.prepare(
25+
"SELECT * FROM cli_auth_codes WHERE code = ? AND status = 'pending' AND expires_at > datetime('now')"
26+
)
27+
.bind(code)
28+
.first<{ id: string }>();
29+
30+
if (!row) return json({ error: 'Invalid or expired code' }, { status: 400 });
31+
32+
const tokenId = generateId();
33+
const tokenValue = 'obt_' + crypto.randomUUID().replace(/-/g, '');
34+
35+
try {
36+
await env.DB.prepare(
37+
`INSERT INTO api_tokens (id, user_id, token, name, expires_at)
38+
VALUES (?, ?, ?, 'cli', datetime('now', '+90 days'))`
39+
)
40+
.bind(tokenId, user.id, tokenValue)
41+
.run();
42+
43+
await env.DB.prepare(
44+
"UPDATE cli_auth_codes SET user_id = ?, token_id = ?, status = 'approved' WHERE id = ?"
45+
)
46+
.bind(user.id, tokenId, row.id)
47+
.run();
48+
} catch (e) {
49+
return json({ error: 'Database error: ' + (e as Error).message }, { status: 500 });
50+
}
51+
52+
return json({ success: true });
53+
};
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { json } from '@sveltejs/kit';
2+
import type { RequestHandler } from './$types';
3+
4+
export const GET: RequestHandler = async ({ platform, url }) => {
5+
const env = platform?.env;
6+
if (!env) return json({ error: 'Platform env not available' }, { status: 500 });
7+
8+
const code_id = url.searchParams.get('code_id');
9+
if (!code_id) return json({ error: 'code_id is required' }, { status: 400 });
10+
11+
const row = await env.DB.prepare('SELECT * FROM cli_auth_codes WHERE id = ?')
12+
.bind(code_id)
13+
.first<{ id: string; code: string; user_id: string | null; token_id: string | null; status: string; expires_at: string }>();
14+
15+
if (!row) return json({ status: 'expired' });
16+
17+
if (row.expires_at < new Date().toISOString()) {
18+
return json({ status: 'expired' });
19+
}
20+
21+
if (row.status === 'pending') {
22+
return json({ status: 'pending' });
23+
}
24+
25+
if (row.status === 'approved' && row.token_id && row.user_id) {
26+
const token = await env.DB.prepare('SELECT * FROM api_tokens WHERE id = ?')
27+
.bind(row.token_id)
28+
.first<{ id: string; token: string; expires_at: string }>();
29+
30+
const user = await env.DB.prepare('SELECT username FROM users WHERE id = ?')
31+
.bind(row.user_id)
32+
.first<{ username: string }>();
33+
34+
await env.DB.prepare("UPDATE cli_auth_codes SET status = 'used' WHERE id = ?")
35+
.bind(code_id)
36+
.run();
37+
38+
return json({
39+
status: 'approved',
40+
token: token?.token,
41+
username: user?.username,
42+
expires_at: token?.expires_at
43+
});
44+
}
45+
46+
return json({ status: 'expired' });
47+
};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { json } from '@sveltejs/kit';
2+
import type { RequestHandler } from './$types';
3+
import { generateId } from '$lib/server/auth';
4+
5+
export const POST: RequestHandler = async ({ platform, request }) => {
6+
const env = platform?.env;
7+
if (!env) return json({ error: 'Platform env not available' }, { status: 500 });
8+
9+
let body;
10+
try {
11+
body = await request.json();
12+
} catch {
13+
return json({ error: 'Invalid request body' }, { status: 400 });
14+
}
15+
16+
const { code } = body;
17+
if (!code || typeof code !== 'string') {
18+
return json({ error: 'Code is required' }, { status: 400 });
19+
}
20+
21+
const code_id = generateId();
22+
23+
try {
24+
await env.DB.prepare(
25+
`INSERT INTO cli_auth_codes (id, code, status, expires_at)
26+
VALUES (?, ?, 'pending', datetime('now', '+10 minutes'))`
27+
)
28+
.bind(code_id, code)
29+
.run();
30+
} catch (e) {
31+
return json({ error: 'Database error: ' + (e as Error).message }, { status: 500 });
32+
}
33+
34+
return json({ code_id });
35+
};

0 commit comments

Comments
 (0)