Consent Flows
Implement user consent for KYA-OS agent actions
Overview
When an AI agent requests access to a user's resources via OAuth or another authentication method, the user sees a consent page before authorization is granted. Checkpoint provides a customizable consent flow that clearly communicates what the agent is requesting and gives the user full control over what to approve.
The consent UI is built with @kya-os/consent — a library of Web Components powered by Lit. Because it uses the Web Components standard, the library renders natively in any framework or no framework at all — React, Next.js, Vue, Angular, Svelte, or vanilla JavaScript.
Built on Web Components — renders natively in any framework
Prerequisites
- A Checkpoint project with at least one authentication method configured. See Credentials for how to find your Project ID and API key in Installations.
- To embed or preview the
@kya-os/consentcomponents in your own app (rather than just customizing the hosted page), a Node/npm project to install the package into.
Live Preview
Switch between authentication modes to see how each consent screen appears to users:
Permission Request
Allow Shopping Assistant to access your account
This agent is requesting the following permissions:
These are live previews of the consent screens users see when authorizing an agent.
How Consent Works
1. Agent initiates authorization request
2. User is redirected to the consent page
3. Consent page displays:
- Agent identity (DID and display name)
- Requested scopes in human-readable form
- Your application branding
- Authentication method (OAuth button, login form, or simple approve)
4. User authenticates (if required) and approves or denies
5. If approved → delegation is created, agent receives authorization
6. If denied → agent receives an error, no delegation is createdConsent Page
The consent page is hosted by Checkpoint and shows:
- Your application name and logo (configured in dashboard)
- Agent identity — The requesting agent's DID and display name
- Requested permissions — Human-readable descriptions of each scope
- Authentication UI — Varies by auth method (OAuth button, credentials form, or approve/deny)
Users can review exactly what they're granting before making a decision.
Customizing the Consent Page
Via the Dashboard
- Navigate to Policy → Auth (
/dashboard/{orgId}/{projectId}/policy/auth) — the consent editor lives here - Configure:
- Application display name and logo
- Custom description text
- Scope descriptions (human-readable wording for each permission)
- Primary theme color
- Save — the hosted consent page picks up your changes
A few settings have not moved yet: Terms of Service / Privacy Policy URLs and the secondary brand
color are still edited on the legacy Control Access surface (/dashboard/{orgId}/{projectId} /control-access/consent).
Via the API
The consent configuration is read-only over the API — GET returns the current config, and there is no write endpoint (a PUT returns 405). Edits happen in the dashboard consent editor (Policy → Auth).
curl -X GET https://kya.vouched.id/api/v1/bouncer/projects/{projectId}/consent-config \
-H "X-API-Key: $AGENTSHIELD_API_KEY" \
-H "X-Project-Id: {projectId}"The response data contains the config object (branding, terms, UI settings, custom fields) plus metadata (version, updatedAt, cacheVersion) — useful for KYA-OS servers that render the consent screen themselves.
Public Configuration Endpoint
The consent page fetches configuration from a public endpoint (no API key required):
curl -X GET https://kya.vouched.id/api/public/consent-config/{projectId}Scope Descriptions
Map technical scope identifiers to user-friendly descriptions. This helps users understand what they're approving:
| Scope | Description Shown to User |
|---|---|
files:read | "View your files and documents" |
files:write | "Create, edit, and upload files" |
cart:write | "Add and remove items from your cart" |
payment:process | "Complete purchases on your behalf" |
Scopes without configured descriptions will display the raw scope identifier (e.g., files:write)
to the user. Always provide human-readable descriptions for a better user experience.
Capabilities
Beyond raw scope descriptions, a project's consentConfig can define a richer, humanized capability layout: named, described permission rows grouped into sections, each with an icon, a risk level, and the Cedar policy fragment bound to the issued delegation when the user approves it. When capabilities is set, the hosted consent page renders this layout instead of the raw scope checkboxes above. Capabilities are saved as part of your project's Bouncer configuration, validated by the same schema as the rest of consentConfig.
A capability group:
| Field | Type | Description |
|---|---|---|
id | string | Lowercase group id (a-z0-9._-) |
label | string | Section heading shown on the consent screen |
capabilities | Capability array | 1 to 20 capabilities in this group |
A capability:
| Field | Type | Description |
|---|---|---|
id | string | Lowercase capability id (a-z0-9._-) |
label | string | Name shown to the user (max 80 chars) |
description | string | What the agent can do (max 280 chars) |
icon | 'search' | 'cart' | 'card' | 'pin' | 'pin-new' | 'shield' | 'key' | 'tools' | 'user' | 'calendar' | 'lock' | 'eye' | 'send' | 'package' | 'neutral' | Icon shown next to the capability |
riskLevel | 'low' | 'medium' | 'high' | 'critical' | How sensitive this grant is |
defaultOn | boolean | Whether the checkbox starts checked |
cedar | string | Cedar policy fragment (permit (...) or forbid (...)) bound at issuance |
scopes | string array | 1 to 20 underlying scope identifiers this capability grants |
category | string, optional | Freeform grouping tag |
The riskLevel / defaultOn floor
A high or critical capability can never be saved with defaultOn: true. This is a hard floor enforced when the configuration is saved, not a client-side check: a checkbox for a grant that requires step-up trust cannot ship pre-checked, regardless of how the capability is otherwise configured.
If a saved capability violates this, the write is rejected before anything reaches storage, with a 400 response shaped like:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid Bouncer configuration",
"details": [
{
"message": "Capability \"payment-charge\" has riskLevel \"critical\" and cannot default on. High/critical capabilities must be structurally unmintable at first contact (First-Contact Scope Cap section 3, Rule 1)."
}
]
}
}The message names the offending capability's id directly, so a save with many capabilities points at the exact row to fix. Resolve it by either lowering riskLevel or setting defaultOn: false, then save again.
This floor applies to every save through this endpoint, not just an edit to the capability itself. A stored config that already violates it (from before this floor existed) will also reject an unrelated resave that round-trips it unchanged — including the dashboard's Cache Refresh action, which resubmits the whole stored config to bump a cache-busting timestamp. Fix the violating capability first; Cache Refresh will succeed again once it's resolved.
Consent State Management
Checkpoint manages consent state automatically:
- Pending — User has been shown the consent page but hasn't responded
- Approved — User approved, delegation created
- Denied — User denied, no delegation created
- Expired — Consent page timed out without a response
Remembering Consent
Consent is remembered automatically. When an agent returns with an active, unexpired delegation for the same project (and user), Checkpoint resolves and reuses that delegation instead of showing the consent page again. Revoke the delegation to force a fresh consent flow.
Redirect Handling
After the user approves or denies consent:
- Approved: Redirects to the agent's
redirect_uriwith an authorization code - Denied: Redirects to the agent's
redirect_uriwith anerror=access_deniedparameter
// Approved
https://agent.example.com/callback?code=auth_xyz&state=random_state
// Denied
https://agent.example.com/callback?error=access_denied&state=random_stateConsent Components
The consent UI is built with @kya-os/consent, a Web Component library powered by Lit. React wrappers are available via @kya-os/consent/react, and the raw custom elements (<mcp-consent>, <consent-shell>, etc.) work in any framework — Vue, Angular, Svelte, or plain HTML. You can use these components to embed consent experiences in your own applications or to preview the flow during development.
Install the package before using any of the components below:
npm install @kya-os/consentAvailable Components
| Component | Purpose |
|---|---|
McpConsentReact | Full consent flow (composite — includes all below) |
ConsentShellReact | Container with branding (logo, colors, title) |
ConsentPermissionsReact | Scope/permission list with optional checkboxes |
ConsentButtonReact | Styled action button (Allow / Deny) |
ConsentCheckboxReact | Checkbox with label (terms acceptance) |
ConsentInputReact | Text input (username, email, password fields) |
ConsentTermsReact | Terms & privacy policy checkbox with links |
ConsentOAuthButtonReact | OAuth provider sign-in button |
Using the Full Consent Component
The McpConsentReact component renders the complete consent flow for any authentication method:
import { McpConsentReact } from '@kya-os/consent/react';
<McpConsentReact
config={{
branding: {
primaryColor: '#2563eb',
logo: { url: 'https://example.com/logo.png', alt: 'My App' },
},
ui: {
title: 'Grant Access',
description: 'Allow this agent to access your account',
},
terms: {
termsText: 'Terms of Service',
termsUrl: 'https://example.com/terms',
privacyText: 'Privacy Policy',
privacyUrl: 'https://example.com/privacy',
},
}}
tool="checkout"
scopes={['cart:read', 'cart:write', 'payment:process']}
agentDid="did:key:z6Mk..."
agentName="Shopping Assistant"
onApprove={(e) => console.log('Approved:', e.detail)}
onDeny={() => console.log('Denied')}
/>;Building a Custom Consent Page
Compose individual components for a custom layout:
import {
ConsentShellReact,
ConsentPermissionsReact,
ConsentTermsReact,
ConsentButtonReact,
} from '@kya-os/consent/react';
<ConsentShellReact pageTitle="Permission Request" companyName="Acme Corp" primaryColor="#1e2d57">
<div slot="content">
<ConsentPermissionsReact
scopes={[
{ id: 'files:read', label: 'Read your files', required: true },
{ id: 'files:write', label: 'Create and edit files' },
]}
interactive
selectAll
iconStyle="shield"
/>
<ConsentTermsReact
text="Terms of Service"
url="https://example.com/terms"
privacyText="Privacy Policy"
privacyUrl="https://example.com/privacy"
required
/>
</div>
<div slot="footer">
<ConsentButtonReact variant="secondary">Cancel</ConsentButtonReact>
<ConsentButtonReact variant="primary">Allow Access</ConsentButtonReact>
</div>
</ConsentShellReact>;CSS Theming
The consent components respect CSS custom properties for consistent branding:
:root {
--consent-primary: #2563eb; /* Primary brand color */
--consent-secondary: #dbeafe; /* Secondary/accent color */
}These are automatically set when you configure primaryColor and secondaryColor on ConsentShellReact or in the config.branding object.
Testing Consent Flows
During development, you can preview the consent screens from the dashboard:
- Open Policy → Auth (
/dashboard/{orgId}/{projectId}/policy/auth) — the consent and success screens are previewed inline as you edit - Test with different scope combinations
- Switch between authentication methods to preview each mode
The full connect-page preview (the hosted page exactly as your users see it) still lives on the legacy Control Access surface: /dashboard/{orgId}/{projectId}/control-access/consent?tab=connect.
The consent page preview uses sample data. In production, the actual agent DID and requested scopes are displayed.
Testing with Components
With @kya-os/consent installed (see Consent Components), import the React wrappers and render with test data:
import { McpConsentReact } from '@kya-os/consent/react';
// Use previewStep to render specific stages
<McpConsentReact
previewStep="consent"
config={yourConfig}
scopes={['files:read', 'files:write']}
agentName="Test Agent"
/>;Next Steps
- Authentication Methods — Choose the auth method for your consent flow
- OAuth Integration — The full OAuth flow that triggers consent
- Tool Protection — Define which scopes map to which tools
- Managing Delegations — Delegations created after consent
