(app: Application)
| 54 | } |
| 55 | |
| 56 | setupRoutes(app: Application): void { |
| 57 | const loginHtml = this.loadLoginPage(); |
| 58 | |
| 59 | // GET /auth/login — serve the API key login form |
| 60 | app.get("/auth/login", (_req, res) => { |
| 61 | res.setHeader("Content-Type", "text/html"); |
| 62 | res.send(loginHtml); |
| 63 | }); |
| 64 | |
| 65 | // POST /auth/login — validate key, create encrypted session |
| 66 | app.post( |
| 67 | "/auth/login", |
| 68 | // express.urlencoded is registered in pty-server.ts before setupRoutes |
| 69 | (req: Request, res: Response) => { |
| 70 | const apiKey = (req.body as Record<string, string>)?.api_key?.trim() ?? ""; |
| 71 | |
| 72 | if (!apiKey.startsWith("sk-ant-")) { |
| 73 | res.setHeader("Content-Type", "text/html"); |
| 74 | res.status(400).send( |
| 75 | loginHtml.replace( |
| 76 | "<!--ERROR-->", |
| 77 | `<p class="error">Invalid API key format. Keys must start with <code>sk-ant-</code>.</p>`, |
| 78 | ), |
| 79 | ); |
| 80 | return; |
| 81 | } |
| 82 | |
| 83 | const userId = deriveUserId(apiKey); |
| 84 | const isAdmin = this.adminUsers.has(userId); |
| 85 | const encryptedApiKey = this.store.encrypt(apiKey); |
| 86 | |
| 87 | const sessionId = this.store.create({ |
| 88 | userId, |
| 89 | isAdmin, |
| 90 | encryptedApiKey, |
| 91 | }); |
| 92 | |
| 93 | this.store.setCookie(res as unknown as import("http").ServerResponse, sessionId); |
| 94 | |
| 95 | const next = (req.query as Record<string, string>)?.next; |
| 96 | res.redirect(next && next.startsWith("/") ? next : "/"); |
| 97 | }, |
| 98 | ); |
| 99 | |
| 100 | // POST /auth/logout — destroy session |
| 101 | app.post("/auth/logout", (req, res) => { |
| 102 | const id = this.store.getIdFromRequest(req as unknown as IncomingMessage); |
| 103 | if (id) this.store.delete(id); |
| 104 | this.store.clearCookie(res as unknown as import("http").ServerResponse); |
| 105 | res.redirect("/auth/login"); |
| 106 | }); |
| 107 | } |
| 108 | |
| 109 | requireAuth(req: Request, res: Response, next: NextFunction): void { |
| 110 | const user = this.authenticate(req as unknown as IncomingMessage); |
nothing calls this directly
no test coverage detected