feat: add MCP endpoint and API key management
- Add ApiKey Prisma model (SHA-256 hash, prefix, lastUsedAt) with migration - Implement ApiKeyService (create, list, delete, verify) - Extend requireAuth middleware to accept sk_-prefixed API keys alongside JWTs - Add GET/POST /api-keys routes for creating and revoking keys - Add stateless Streamable HTTP MCP server at POST/GET /mcp exposing all 20 time-tracking tools (clients, projects, time entries, timer, statistics, client targets and corrections) - Frontend: ApiKey types, apiKeys API module, useApiKeys hook - Frontend: ApiKeysPage with key table, one-time raw-key reveal modal, and inline revoke confirmation - Wire /api-keys route and add API Keys link to Management dropdown in Navbar
This commit is contained in:
@@ -2,6 +2,9 @@ import { Request, Response, NextFunction } from 'express';
|
||||
import { prisma } from '../prisma/client';
|
||||
import type { AuthenticatedRequest, AuthenticatedUser } from '../types';
|
||||
import { verifyBackendJwt } from '../auth/jwt';
|
||||
import { ApiKeyService } from '../services/apiKey.service';
|
||||
|
||||
const apiKeyService = new ApiKeyService();
|
||||
|
||||
export async function requireAuth(
|
||||
req: AuthenticatedRequest,
|
||||
@@ -17,11 +20,33 @@ export async function requireAuth(
|
||||
return next();
|
||||
}
|
||||
|
||||
// 2. Bearer JWT auth (iOS / native clients)
|
||||
// 2. Bearer token auth (JWT or API key)
|
||||
const authHeader = req.headers.authorization;
|
||||
if (authHeader?.startsWith('Bearer ')) {
|
||||
const token = authHeader.slice(7);
|
||||
console.log(`${tag} -> Bearer token present (first 20 chars: ${token.slice(0, 20)}…)`);
|
||||
|
||||
// 2a. API key — detected by the "sk_" prefix
|
||||
if (token.startsWith('sk_')) {
|
||||
try {
|
||||
const user = await apiKeyService.verify(token);
|
||||
if (!user) {
|
||||
console.warn(`${tag} -> API key verification failed: key not found`);
|
||||
res.status(401).json({ error: 'Unauthorized: invalid API key' });
|
||||
return;
|
||||
}
|
||||
req.user = user;
|
||||
console.log(`${tag} -> API key auth OK (user: ${req.user.id})`);
|
||||
return next();
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
console.warn(`${tag} -> API key verification error: ${message}`);
|
||||
res.status(401).json({ error: `Unauthorized: ${message}` });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2b. JWT (iOS / native clients)
|
||||
try {
|
||||
req.user = verifyBackendJwt(token);
|
||||
console.log(`${tag} -> JWT auth OK (user: ${req.user.id})`);
|
||||
|
||||
Reference in New Issue
Block a user