Initial commit: Elydora Open Source — self-hosted audit platform for AI agents
Full self-hosted port of the Elydora tamper-evident audit protocol: - Hono API server with PostgreSQL, Redis, MinIO adapters - Ed25519 signed operations, chain hashing, Merkle epoch rollups - PBKDF2 password hashing, HMAC-SHA256 JWT, 5-role RBAC - Next.js management console - Node.js, Python, and Go SDKs - Docker Compose for local deployment - Helm chart for Kubernetes - Comprehensive security hardening and audit fixes applied Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
15547a8c35
commit
e390e68594
207 files changed
+32790
-63
No files matched your search
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Auth routes — registration, login, profile retrieval, and token refresh.
|
||||
*
|
||||
* /register and /login are public (no auth required).
|
||||
* /me and /refresh require a valid JWT.
|
||||
*/
|
||||
|
||||
import { Hono } from 'hono';
|
||||
import type { Env, AppVariables } from '../types.js';
|
||||
import { authMiddleware } from '../middleware/auth.js';
|
||||
import { registerUser, loginUser, getMe, issueJWT, issueApiToken } from '../services/auth-service.js';
|
||||
import type { RbacRole } from '../shared/index.js';
|
||||
import { DEFAULT_JWT_TTL_SECONDS } from '../shared/index.js';
|
||||
import { getMessage } from '../i18n/messages.js';
|
||||
|
||||
const auth = new Hono<{ Bindings: Env; Variables: AppVariables }>();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// POST /v1/auth/register — Create a new user and organization
|
||||
// ---------------------------------------------------------------------------
|
||||
auth.post('/register', async (c) => {
|
||||
const body = await c.req.json();
|
||||
const { email, password, display_name, org_name } = body;
|
||||
|
||||
const result = await registerUser(
|
||||
c.env.ELYDORA_DB,
|
||||
c.env.JWT_SECRET,
|
||||
email,
|
||||
password,
|
||||
display_name,
|
||||
org_name,
|
||||
);
|
||||
|
||||
return c.json(result, 201);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// POST /v1/auth/login — Authenticate and receive a JWT
|
||||
// ---------------------------------------------------------------------------
|
||||
auth.post('/login', async (c) => {
|
||||
const body = await c.req.json();
|
||||
const { email, password } = body;
|
||||
|
||||
const result = await loginUser(c.env.ELYDORA_DB, c.env.JWT_SECRET, email, password);
|
||||
|
||||
return c.json(result, 200);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GET /v1/auth/me — Get current user profile (auth required)
|
||||
// ---------------------------------------------------------------------------
|
||||
auth.get('/me', authMiddleware, async (c) => {
|
||||
const userId = c.get('actor');
|
||||
const user = await getMe(c.env.ELYDORA_DB, userId);
|
||||
|
||||
return c.json({ user }, 200);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// POST /v1/auth/refresh — Issue a new JWT (auth required)
|
||||
// ---------------------------------------------------------------------------
|
||||
auth.post('/refresh', authMiddleware, async (c) => {
|
||||
const now = Date.now();
|
||||
const token = await issueJWT(
|
||||
{
|
||||
sub: c.get('actor'),
|
||||
org_id: c.get('org_id'),
|
||||
role: c.get('role') as RbacRole,
|
||||
iat: Math.floor(now / 1000),
|
||||
exp: Math.floor(now / 1000) + DEFAULT_JWT_TTL_SECONDS,
|
||||
},
|
||||
c.env.JWT_SECRET,
|
||||
);
|
||||
|
||||
return c.json({ token }, 200);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// POST /v1/auth/token — Issue an API token with custom TTL (auth required)
|
||||
// ---------------------------------------------------------------------------
|
||||
auth.post('/token', authMiddleware, async (c) => {
|
||||
const body = await c.req.json();
|
||||
const { ttl_seconds } = body;
|
||||
|
||||
if (ttl_seconds !== null && ttl_seconds !== undefined) {
|
||||
if (typeof ttl_seconds !== 'number' || ttl_seconds <= 0) {
|
||||
const lang = c.get('lang') ?? 'en';
|
||||
return c.json({ error: { code: 'VALIDATION_ERROR', message: getMessage('auth.ttlMustBeNumber', lang), request_id: '' } }, 400);
|
||||
}
|
||||
}
|
||||
|
||||
const result = await issueApiToken(
|
||||
c.get('actor'),
|
||||
c.get('org_id'),
|
||||
c.get('role') as RbacRole,
|
||||
c.env.JWT_SECRET,
|
||||
ttl_seconds ?? null,
|
||||
);
|
||||
|
||||
return c.json(result, 200);
|
||||
});
|
||||
|
||||
export { auth };
|
||||
Reference in new issue
Block a user