Identity & SSO
Identity providers, JWT validation, JWKS caching, external IDs, and directory sync.
On this page
Overview
Enterprise networks can integrate with external identity providers (IDPs) to centralize authentication. Instead of relying solely on Pilot’s built-in Ed25519 keys, agents can present tokens from your organization’s IDP — OIDC, SAML, Entra ID, LDAP, or a custom webhook — and the registry validates them before granting access.
Identity integration is per-network. Each network can have its own IDP configuration, allowing different teams or environments to use different providers.
Supported providers
| Provider | Type value | Description |
|---|---|---|
| OIDC | oidc | OpenID Connect — standard JWT-based SSO. Works with Auth0, Okta, Google, etc. |
| SAML | saml | Security Assertion Markup Language — XML-based enterprise SSO. |
| Entra ID | entra_id | Microsoft Entra ID (Azure AD) — native integration for Microsoft environments. |
| LDAP | ldap | Lightweight Directory Access Protocol — on-premises directory servers. |
| Webhook | webhook | Custom HTTP callback — your own verification endpoint for non-standard systems. |
Configuration
Configure an identity provider with the set_idp_config protocol command:
# OIDC example
{
"command": "set_idp_config",
"type": "oidc",
"url": "https://accounts.example.com/.well-known/openid-configuration",
"client_id": "pilot-network-prod",
"admin_token": "your-admin-token"
}
The IDP configuration is stored in the registry and persists across restarts. You can also set the IDP through a blueprint using the identity_provider field.
To query the current configuration:
{
"command": "get_idp_config",
"admin_token": "your-admin-token"
}
An audit event (idp.configured) is emitted when the IDP is set or changed.
JWT validation
The registry includes built-in JWT validation supporting two algorithms:
| Algorithm | Key type | Use case |
|---|---|---|
| RS256 | RSA public key (from JWKS) | Standard OIDC/OAuth2 providers |
| HS256 | Shared secret | Simple integrations, internal services |
Validate a token with the validate_token protocol command:
{
"command": "validate_token",
"token": "eyJhbGciOiJSUzI1NiIs...",
"admin_token": "your-admin-token"
}
Returns: valid (bool), claims (the decoded JWT claims if valid), or error (string describing why validation failed).
Validated claims
The validator checks:
- Signature — verified against the JWKS public key (RS256) or shared secret (HS256)
- Expiration (
exp) — token must not be expired - Not-before (
nbf) — token must not be used before its valid time - Issued-at (
iat) — checked for reasonableness - Issuer (
iss) — must match the configured IDP URL - Audience (
aud) — must match the configured client ID
JWKS caching
For RS256 tokens, the registry fetches the provider’s JSON Web Key Set (JWKS) to obtain public keys for signature verification. JWKS responses are cached to avoid hitting the provider on every validation.
| Parameter | Value |
|---|---|
| Cache TTL | 5 minutes |
| Max response size | 64 KB |
| Key matching | By kid (Key ID) header in the JWT |
| Refresh | Automatic on cache expiry; on-demand if kid not found in cached set |
If the JWKS endpoint is unreachable and the cache has expired, validation fails with a clear error. The registry does not fall back to accepting unverified tokens.
Security
Algorithm confusion prevention
The validator enforces the expected algorithm based on configuration. If the IDP is configured with RS256, an attacker cannot present an HS256 token signed with the public key (a classic algorithm confusion attack). The alg header in the JWT must match the configured algorithm.
Clock skew tolerance
A 60-second clock skew tolerance is applied to all time-based claims (exp, nbf, iat). This accommodates minor clock differences between the IDP and the registry without opening a significant window for expired tokens.
Webhook identity
For systems that don’t support OIDC or SAML, configure a webhook identity provider. The registry sends the agent’s credentials to your HTTP endpoint, and your endpoint returns an approval or rejection.
# Configure a webhook IDP
{
"command": "set_idp_config",
"type": "webhook",
"url": "https://auth.example.com/verify-agent",
"admin_token": "your-admin-token"
}
Your endpoint receives a POST with the agent’s identity information and must return a JSON response indicating whether the agent is authorized.
External IDs
Map agents to external identity systems using external IDs:
# Set an external ID for an agent
{
"command": "set_external_id",
"node_id": 5,
"external_id": "[email protected]",
"admin_token": "your-admin-token"
}
# Look up an agent’s identity
{
"command": "get_identity",
"node_id": 5,
"admin_token": "your-admin-token"
}
External IDs are free-form strings — email addresses, UPNs, LDAP DNs, or any identifier from your external system. They are stored in the registry and included in audit events. An identity.external_id_set audit event is emitted on change, recording both old and new values.
Directory sync
Directory sync pushes entries from an external directory (AD, Entra ID, LDAP) to the registry, automatically provisioning and deprovisioning network members.
Sync operation
{
"command": "directory_sync",
"network_id": 1,
"entries": [
{
"external_id": "[email protected]",
"node_id": 5,
"role": "admin",
"tags": ["frontend", "us-east"]
},
{
"external_id": "[email protected]",
"node_id": 8,
"role": "member"
}
],
"remove_unlisted": true,
"admin_token": "your-admin-token"
}
What sync does
- Matches entries — each entry is matched to a registered node by
node_id - Joins to network — nodes not already in the network are added
- Maps roles — the
rolefield sets the RBAC role (admin or member). Owner role cannot be assigned through sync. - Sets external IDs — the
external_idfield is stored for identity mapping - Applies tags — optional
tagsfield sets the node’s capability tags - Removes unlisted — if
remove_unlistedis true, members not in the entries list are kicked from the network
RBAC pre-assignments
Directory sync supports role pre-assignment: when a new member is added through sync, they receive their assigned role immediately instead of defaulting to member. This is useful for provisioning admin roles in bulk.
Query sync status
{
"command": "get_directory_status",
"network_id": 1,
"admin_token": "your-admin-token"
}
Returns the last sync timestamp, number of entries processed, and any errors encountered.
A directory.synced audit event is emitted after each sync, recording the network ID, number of entries added, removed, and role changes.
Pilot Protocol