auth/permit_offer_actions.ts

Permit offer RPC action handlers — the consentful-permits action surface.

Seven actions: six offer-lifecycle methods (create / accept / decline / retract / list / history) plus permit_revoke (admin-only). All mount on a consumer's JSON-RPC endpoint via create_rpc_endpoint. The action specs themselves live in auth/permit_offer_action_specs.ts. Mutations declare side_effects: true so the RPC dispatcher wraps the handler in a DB transaction; permit_offer_list and permit_offer_history declare side_effects: false so they are addressable via GET.

Authorization: - permit_offer_create — the grantor must hold an active permit for the role being offered, and that role must be web_grantable. Consumers needing a richer policy (e.g., "teacher may offer student in *their* classroom") pass an authorize callback that overrides the default. - permit_offer_accept / permit_offer_decline — keyed to the caller's account; query_* helpers enforce the IDOR guard. - permit_offer_retract — keyed to the caller's actor. - permit_offer_list / permit_offer_history — self by default; {account_id} is admin-only. - permit_revoke — spec-level auth: {role: 'admin'}; the RPC dispatcher rejects non-admin callers before the handler runs. web_grantable gate prevents revoking keeper/daemon-scoped roles via this surface. Keys on actor_id to survive multi-actor accounts.

Audit events are emitted in-transaction by the query layer (atomic with the permit write on accept/revoke) or by the handler via audit_log_fire_and_forget for single-event lifecycle transitions. on_audit_event (SSE broadcast) fires post-commit in both paths.

WS notifications fan out post-commit via emit_after_commit when a notification_sender is wired: offer lifecycle transitions notify the counterparty, permit_revoke notifies the revokee plus each superseded pending offer's grantor.

Declarations
#

5 declarations

view source

authorize_admin_or_holder
#

auth/permit_offer_actions.ts view source

(auth: RequestContext, input: { to_account_id: string; role: string; scope_id: string | null; }, deps: Pick<RouteFactoryDeps, "log">, ctx: ActionContext): boolean | Promise<...>

Authorization callback that admits any admin and otherwise falls back to the symmetric default (caller must hold the offered role globally).

The web_grantable filter in create_handler runs before the authorize callback, so this never sees non-web-grantable roles. Drop into create_permit_offer_actions({authorize: authorize_admin_or_holder}) (or any factory that forwards authorize, e.g. create_standard_rpc_actions) for the common "admins offer anything; users offer what they hold" pattern. Scope-aware policies (e.g. classroom_teacher offering classroom_student in their own scope) wrap this and short-circuit true before delegating.

auth

input

type { to_account_id: string; role: string; scope_id: string | null; }

deps

type Pick<RouteFactoryDeps, "log">

ctx

returns

boolean | Promise<boolean>

create_permit_offer_actions
#

auth/permit_offer_actions.ts view source

(deps: PermitOfferActionDeps, options?: PermitOfferActionOptions): RpcAction[]

Create the seven permit-offer RPC actions (six offer-lifecycle methods plus permit_revoke).

deps

PermitOfferActionDepslog, on_audit_event, optional audit_log_config (slice of AppDeps); optional notification_sender for WS fan-out

options

role schema, default TTL, authorization override

default {}

returns

RpcAction[]

the RpcAction array to spread into a create_rpc_endpoint call

PermitOfferActionDeps
#

auth/permit_offer_actions.ts view source

PermitOfferActionDeps

Dependencies for create_permit_offer_actions.

notification_sender is optional — when absent, WS fan-out is silently skipped. Consumers wiring BackendWebsocketTransport assign its instance directly (the transport's send_to_account signature accepts the broader JsonrpcMessageFromServerToClient, which is contravariantly compatible).

inheritance

extends:
  • Pick< RouteFactoryDeps, 'log' | 'on_audit_event' | 'audit_log_config' >

notification_sender

Optional WS fan-out primitive. null or absent → notifications skipped.

type NotificationSender | null

PermitOfferActionOptions
#

auth/permit_offer_actions.ts view source

PermitOfferActionOptions

roles

Role schema result from create_role_schema(). Defaults to builtin roles only. The role_options map is read for web_grantable lookups.

default_ttl_ms

TTL applied to newly-created offers. Defaults to PERMIT_OFFER_DEFAULT_TTL_MS.

type number

authorize

Custom authorization for permit_offer_create. The default requires the caller to hold an active permit for the offered role *and* the role to be web_grantable. Consumers with richer policies (scope-aware, chained roles) override this.

PermitOfferCreateAuthorize
#

auth/permit_offer_actions.ts view source

PermitOfferCreateAuthorize

Authorization callback for permit_offer_create. Returns true to allow, false to reject (handler converts to forbidden).

Provided with the fully-resolved request context and the parsed input (pre-TTL, pre-normalization). Consumers override the default to implement policies like "teacher may offer classroom_student only in classrooms they teach".

Depends on
#

Imported by
#