feat(api): agent bearer auth middleware
Add lib/api/middleware/agent_auth.js: agentOrOwner accepts the owner token (kind=user actor) or a hashed agent token (kind=agent actor carrying capabilities + scopes). /api router now mounts this in place of ownerOnly so agent tokens become first-class. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
32
lib/api/middleware/agent_auth.js
Normal file
32
lib/api/middleware/agent_auth.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import * as agents from '../../db/repos/agents.js';
|
||||
|
||||
export async function agentOrOwner(req, res, next) {
|
||||
const expectedOwner = process.env.OWNER_TOKEN;
|
||||
if (!expectedOwner) {
|
||||
return res.status(500).json({
|
||||
error: { code: 'no_owner_token', message: 'OWNER_TOKEN not configured' }
|
||||
});
|
||||
}
|
||||
const auth = req.headers.authorization || '';
|
||||
const [scheme, token] = auth.split(' ');
|
||||
if (scheme !== 'Bearer' || !token) {
|
||||
return res.status(401).json({ error: { code: 'unauthorized', message: 'missing bearer token' } });
|
||||
}
|
||||
if (token === expectedOwner) {
|
||||
req.actor = { kind: 'user', id: null };
|
||||
return next();
|
||||
}
|
||||
try {
|
||||
const agent = await agents.verifyToken(token);
|
||||
if (!agent) {
|
||||
return res.status(401).json({ error: { code: 'unauthorized', message: 'invalid token' } });
|
||||
}
|
||||
req.actor = {
|
||||
kind: 'agent',
|
||||
id: agent.id,
|
||||
capabilities: agent.capabilities || {},
|
||||
scopes: agent.scopes || {}
|
||||
};
|
||||
next();
|
||||
} catch (e) { next(e); }
|
||||
}
|
||||
Reference in New Issue
Block a user