The /me endpoint was returning auth.sub (Logto's opaque string) as the user ID, but the frontend and other API endpoints expect numeric DB IDs. This caused "can't access property 'id', w[0] is undefined" after login. Also documents Logto OIDC setup requirements (scopes, env vars) in CLAUDE.md. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
94 lines
2.6 KiB
TypeScript
94 lines
2.6 KiB
TypeScript
import { getAuth } from "@hono/oidc-auth";
|
|
import { zValidator } from "@hono/zod-validator";
|
|
import { Hono } from "hono";
|
|
import { z } from "zod";
|
|
import { updateProfileSchema } from "../../shared/schemas.ts";
|
|
import { parseId } from "../lib/params.ts";
|
|
import { requireAuth } from "../middleware/auth.ts";
|
|
import {
|
|
createApiKey,
|
|
deleteApiKey,
|
|
getOrCreateUser,
|
|
listApiKeys,
|
|
} from "../services/auth.service.ts";
|
|
import { updateProfile } from "../services/profile.service.ts";
|
|
|
|
type Env = { Variables: { db?: any; userId?: number } };
|
|
|
|
const createKeySchema = z.object({ name: z.string().min(1) });
|
|
|
|
const app = new Hono<Env>();
|
|
|
|
// ── Auth Status ──────────────────────────────────────────────────────
|
|
|
|
app.get("/me", async (c) => {
|
|
const auth = await getAuth(c);
|
|
if (auth) {
|
|
const db = c.get("db");
|
|
const user = await getOrCreateUser(db, auth.sub);
|
|
return c.json({
|
|
user: { id: user.id, email: auth.email },
|
|
authenticated: true,
|
|
});
|
|
}
|
|
return c.json({ user: null, authenticated: false });
|
|
});
|
|
|
|
// ── API Key Management (protected) ───────────────────────────────────
|
|
|
|
app.get("/keys", requireAuth, async (c) => {
|
|
const db = c.get("db");
|
|
const userId = c.get("userId")!;
|
|
const keys = await listApiKeys(db, userId);
|
|
return c.json(keys);
|
|
});
|
|
|
|
app.post(
|
|
"/keys",
|
|
requireAuth,
|
|
zValidator("json", createKeySchema),
|
|
async (c) => {
|
|
const db = c.get("db");
|
|
const userId = c.get("userId")!;
|
|
const { name } = c.req.valid("json");
|
|
const result = await createApiKey(db, userId, name);
|
|
|
|
return c.json(
|
|
{
|
|
id: result.id,
|
|
name: result.name,
|
|
key: result.rawKey,
|
|
prefix: result.keyPrefix,
|
|
},
|
|
201,
|
|
);
|
|
},
|
|
);
|
|
|
|
app.delete("/keys/:id", requireAuth, async (c) => {
|
|
const db = c.get("db");
|
|
const userId = c.get("userId")!;
|
|
const id = parseId(c.req.param("id"));
|
|
if (!id) return c.json({ error: "Invalid key ID" }, 400);
|
|
await deleteApiKey(db, userId, id);
|
|
return c.json({ ok: true });
|
|
});
|
|
|
|
// ── Profile Update (protected) ──────────────────────────────────────
|
|
|
|
app.put(
|
|
"/profile",
|
|
requireAuth,
|
|
zValidator("json", updateProfileSchema),
|
|
async (c) => {
|
|
const db = c.get("db");
|
|
const userId = c.get("userId")!;
|
|
const data = c.req.valid("json");
|
|
const updated = await updateProfile(db, userId, data);
|
|
if (!updated) return c.json({ error: "User not found" }, 404);
|
|
return c.json(updated);
|
|
},
|
|
);
|
|
|
|
export const authRoutes = app;
|