auth/self_service_role_actions.ts

Unified self-service role toggle RPC action.

One static request_response action — self_service_role_set — that takes {role, enabled} and toggles a global role_grant on the caller for an allowlisted role. Idempotent in both directions: re-enabling an already-held role returns changed: false; disabling a role the caller doesn't hold returns changed: false.

Eligibility is derived by default from RoleSpec.grant_paths — every role whose grant_paths includes 'self_service' (GRANT_PATH_SELF_SERVICE) is eligible. The factory accepts an optional eligible_roles override (validated against the supplied roles.role_specs at factory time so typos surface at startup instead of at first call) for deployments that want to lock the surface down further than the role spec declares. Roles outside the eligible set are rejected with forbidden + reason role_not_self_service_eligible.

Audit metadata carries self_service: true so admin reviewers can distinguish self-toggled role_grants from admin grants/offers. The role_grant_create / role_grant_revoke metadata schemas declare self_service: z.boolean().optional() explicitly, so the field is part of the documented schema surface and is round-trip-validated by query_audit_log.

Static method name — role lives in the input, not the method name — so the spec is codegen-compatible (satisfies RequestResponseActionSpec) and the surface stays constant as consumers add eligible roles. Mirrors the existing role_grant_offer_create({role}) precedent rather than generating per-role methods.

Specs and schemas live in auth/self_service_role_action_specs.ts so client-side codegen can import the surface without dragging in the query layer.

Declarations
#

2 declarations

view source

create_self_service_role_actions
#

auth/self_service_role_actions.ts view source

(deps: Pick<RouteFactoryDeps, "log" | "audit">, options?: SelfServiceRoleActionsOptions): RpcAction[]

Build the unified self-service role toggle RPC action.

deps

RouteFactoryDeps (log, audit, …); audit.emit writes audit rows via the captured pool. The bound emitter encapsulates on_audit_event fan-out and the optional AuditLogConfig.

type Pick<RouteFactoryDeps, "log" | "audit">

options

optional eligible-role override plus optional role schema for default-eligibility derivation

default {}

returns

RpcAction[]

the RpcAction array to spread into a create_rpc_endpoint call

throws

  • Error - at factory time if any `eligible_roles` entry is missing from `options.roles.role_specs`

SelfServiceRoleActionsOptions
#

auth/self_service_role_actions.ts view source

SelfServiceRoleActionsOptions

eligible_roles

Optional override allowlist of role strings eligible for self-service. When omitted, eligibility is derived from roles.role_specs (or BUILTIN_ROLE_SPECS_BY_NAME when roles is also omitted) by selecting every role whose RoleSpec.grant_paths includes 'self_service'. Pass an empty array to lock the surface down (every call comes back as forbidden with reason role_not_self_service_eligible).

When supplied alongside roles, every entry is checked against roles.role_specs at factory time so typos throw at startup.

type ReadonlyArray<string>

roles

Optional role schema. Drives default eligibility derivation from RoleSpec.grant_paths and validates the eligible_roles override (when supplied) against the registered role set.

Depends on
#