@adonis-agora/agent
Version:
Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.
40 lines • 1.91 kB
JavaScript
import { readContextAccessor, tenantIdFromContext, userRefFromContext } from './agora-context.js';
/**
* An {@link ActorResolver} that reads the caller from the Agora context authkit populates
* (`userRef`, `tenantId`) and takes the caller's roles from authz (`effectiveRoles`). It ignores
* the transport `req` and reads the active Agora context store, so it needs no `ctx.auth` and is
* framework-agnostic.
*
* Security posture — FAIL-CLOSED: no `userRef.id` in context → throws (the provider replies 401).
* An identity is never fabricated. The context `userRef` is passed straight to `effectiveRoles`;
* authz reads its `.id`/`.type` and re-reads `globalRoles` from the same context, so global roles
* (e.g. `ADMIN`) land in the result with no extra wiring.
*/
export class AuthzActorResolver {
#authz;
constructor(config) {
this.#authz = config.authz;
}
async resolve(_req) {
const accessor = readContextAccessor();
const ref = userRefFromContext(accessor);
if (ref?.id === undefined || ref.id === null || String(ref.id).length === 0) {
throw new Error('authzActorResolver: no authenticated identity in @agora/context. The agent route must ' +
'sit behind authkit auth middleware (which populates the context), with the ' +
'@adonis-agora/context provider registered.');
}
const tenantId = tenantIdFromContext(accessor);
const scope = tenantId ? { tenantId } : undefined;
const roles = await this.#authz.effectiveRoles(ref, scope);
return {
id: String(ref.id),
roles,
...(tenantId ? { tenantRef: tenantId } : {}),
};
}
}
/** Factory for {@link AuthzActorResolver}. */
export function authzActorResolver(config) {
return new AuthzActorResolver(config);
}
//# sourceMappingURL=authz-actor-resolver.js.map