Skip to content

Add a token claims hook

Add a token claims hook

Add a custom claim to every access token — or block issuance — with a TokenMinting inline hook. Two ways.

In-process (a class in your app)

Write an Action and register it:

use Cbox\Id\ExternalActions\Contracts\Action;
use Cbox\Id\ExternalActions\ValueObjects\ActionContext;
use Cbox\Id\ExternalActions\ValueObjects\ActionResult;

final class AddTenantTier implements Action
{
    public function handle(ActionContext $context): ActionResult
    {
        $orgId = $context->string('organization_id');

        if ($orgId !== null && $this->overQuota($orgId)) {
            return ActionResult::deny('organization is over quota'); // → token endpoint returns access_denied
        }

        return ActionResult::continue(['tenant_tier' => $this->tierFor($orgId)]); // adds a claim
    }
}
// config/cbox-id.php
'external_actions' => [
    'hooks' => [
        'token_minting' => [App\Actions\AddTenantTier::class],
    ],
],

The claim appears on the access token:

{ "iss": "cbox-id", "sub": "…", "scope": "openid", "tenant_tier": "pro" }

Reserved claims (sub, exp, scope, aud, …) can't be overwritten — only new keys land.

External HTTP endpoint

Point the hook at your own HTTPS service instead:

use Cbox\Id\ExternalActions\Contracts\ExternalActions;
use Cbox\Id\ExternalActions\Enums\HookPoint;

$registered = app(ExternalActions::class)->register(HookPoint::TokenMinting, 'https://hooks.acme.com/token');
// Store $registered->secret — shown once — to verify X-Cbox-Signature on your endpoint.

Your endpoint receives a signed POST {"context": {...}} and replies within a few seconds:

{ "action": "continue", "claims": { "tenant_tier": "pro" } }
// or: { "action": "deny", "reason": "organization is over quota" }

Verify the signature before trusting the request:

$expected = hash_hmac('sha256', $request->header('X-Cbox-Timestamp').'.'.$request->getContent(), $secret);
// compare (constant-time) against the v1=… part of X-Cbox-Signature, and reject stale timestamps.

Fail-closed

If your hook is unreachable or errors, issuance is deniedtoken_minting is a gate, and a control that fails open is not a control. If you'd rather issue without the claim than block, set external_actions.fail_policy.token_minting to 'open'. Each hook point has its own default and its own override; see Hook points and Security: external actions.

The other five points

The same two mechanisms gate logins, signups and password changes — post_login, pre_registration, post_registration, pre_password_change, post_password_change. Payloads and fail policies: Hook points.