Skip to main content

identity-guide

Authbridge and Identity

Identity, Authentication, & Authorization

This guide covers identity, authentication, and authorization in the Rossoctl platform. Rossoctl implements a Zero-Trust Architecture that combines SPIFFE/SPIRE workload identity, OAuth2 token exchange, and Keycloak identity management to provide secure, scalable, and dynamic authentication for cloud-native AI agents.

In practice, the Authorization Pattern within the Agentic Platform enables:

  • Machine Identity Management – replacing static credentials with SPIRE-issued JWTs.
  • Secure Delegation – enforcing token exchange to propagate identity across services without excessive permissions.
  • Continuous Verification – ensuring authentication and authorization at each step, preventing privilege escalation.

🏗️ Architecture Overview

Zero-Trust Identity Foundation

Rossoctl checks of the Authorization header on incoming A2A requests before they reach your agent, without code changes. Rossoctl includes plugins for RFC 8693 token exchange and LLM traffic filters that can be applied to your workloads without code changes.

Rossoctl's identity architecture is built on three core principles:

  1. No Implicit Trust - Every request requires explicit authentication and authorization
  2. Least Privilege Access - Users and workloads receive minimum necessary permissions
  3. Continuous Verification - Identity and permissions are validated at every interaction

Key Components

ComponentPurposeTechnology
SPIFFE/SPIREWorkload Identity & AttestationIndustry-standard workload identity framework
KeycloakIdentity Provider & Access ManagementOAuth2/OIDC compliant identity server
OAuth2 Token ExchangeSecure Token DelegationRFC 8693 token exchange protocol
MCP GatewayProtocol-Level AuthenticationEnvoy-based authentication proxy
Kubernetes RBACContainer-Level AuthorizationNative Kubernetes access controls

🔐 SPIFFE/SPIRE Workload Identity

What is SPIFFE/SPIRE?

SPIFFE (Secure Production Identity Framework For Everyone) provides a universal identity control plane for distributed systems. SPIRE is the production-ready implementation that issues and manages SPIFFE identities.

SPIFFE Identity Format

In Rossoctl, workloads receive SPIFFE identities in the following format:

spiffe://{trust-domain}/ns/{namespace}/sa/{service-account}

Examples:

# Slack Research Agent
spiffe://localtest.me/ns/team/sa/slack-researcher

# Weather Tool
spiffe://localtest.me/ns/team/sa/weather-tool

# GitHub Issue Agent
spiffe://apps.cluster-swkz5.dynamic.redhatworkshops.io/ns/team/sa/github-issue-agent

# MCP Gateway Service
spiffe://apps.cluster-swkz5.dynamic.redhatworkshops.io/ns/gateway-system/sa/mcp-gateway

SVID Types

SPIRE issues SPIFFE Verifiable Identity Documents (SVIDs) in two formats:

  1. X.509 SVID - Certificate-based identity for mTLS
  2. JWT SVID - Token-based identity for HTTP APIs

JWT SVID Structure:

{
"sub": "spiffe://localtest.me/ns/team/sa/slack-researcher",
"aud": "rossoctl",
"exp": 1735689600,
"iat": 1735686000,
"iss": "https://spire-server.spire.svc.cluster.local:8443"
}

SPIRE Environment Validation

To verify SPIRE is properly configured:

1. OIDC Discovery Endpoint

# Check SPIRE OIDC service and
# Verify JWT signing keys
curl http://spire-oidc.localtest.me:8080/keys

3. Workload SVID Validation

# Check if agent has received SVID
kubectl exec -n team deployment/slack-researcher --container authbridge-proxy -- ls -la /opt/
# Should show: svid.pem, svid_key.pem, svid_bundle.pem, jwt_svid.token

🎫 Keycloak Identity Management

Keycloak Architecture in Rossoctl

Keycloak serves as the central identity provider that:

  • Manages user identities and authentication
  • Issues OAuth2/OIDC tokens
  • Tells Cortex if it should enforce role-based access control (RBAC)
  • Facilitates token exchange between services
  • Validates SPIFFE identities for workload authentication

Realm Configuration

rossoctl Realm is configured with:

  • Users: Demo users with different access levels
  • Clients: Each agent/tool is a Keycloak client
  • Roles: Granular permissions (e.g., slack-full-access, github-partial-access)
  • Scopes: Define token audiences and permissions

Client Types

Client TypeAuthenticationPurposeExample
Public ClientNo secretFrontend applicationsrossoctl-ui
Confidential ClientClient secretBackend servicesTraditional services
SPIFFE ClientJWT SVIDWorkload identityspiffe://localtest.me/ns/team/sa/slack-researcher

Keycloak Admin Access

# Access Keycloak Admin Console
# on kind:
open http://keycloak.localtest.me:8080/admin/rossoctl/console/
# on OpenShift:
open "https://$(kubectl get route mcp-proxy -n rossoctl-system -o jsonpath='{.status.ingress[0].host}')"

# Get admin credentials from Kubernetes (if different)
kubectl get secret keycloak-initial-admin -n keycloak -o go-template=\
'Username: {{.data.username | base64decode}} Password: {{.data.password | base64decode}}{{"\n"}}'
# Example output:
# Username: admin Password: XyZ1234!

🛠️ Practical Implementation Guide

Client Registration Process

Keycloak client registration is handled automatically when "☑ Secure with AuthBridge" is selected during deployment. This is fully automatic and requires no manual intervention or init containers.

Internally, registration is handled by the rossoctl-operator's ClientRegistrationReconciler.


🌉 AuthBridge Component

The AuthBridge Component provides a complete, hands-on implementation of Rossoctl's identity and authorization patterns. It combines Client Registration and AuthProxy to demonstrate the full zero-trust authentication flow.

What AuthBridge Demonstrates

CapabilityDescription
Automatic Workload IdentityPod registers itself with Keycloak using SPIFFE ID
Inbound JWT ValidationValidates incoming token signature, expiration, and issuer via JWKS; optionally validates audience. Returns 401 for invalid tokens.
Transparent Token ExchangeSidecar exchanges outbound tokens for correct target audience via Keycloak
Target Service ValidationTarget validates token has correct audience

AuthBridge Architecture

┌─────────────────────────────────────────────────────────────────────────────────┐
│ 1. Operator reconciles AgentRuntime CRs and labels target workloads │
│ 2. Operator registers client with Keycloak (using admin credentials from │
│ operator namespace) and creates credentials secret in agent namespace │
│ 3. (removed) │
│ 4. Agent gets token from Keycloak using credentials from secret │
│ 5. Agent sends request to target with token │
│ 6. Envoy+ext-proc intercepts: validates token signature, expiration, issuer │
│ via JWKS (returns 401 if invalid), then exchanges token for target audience │
│ 7. Target receives request with exchanged token and validates audience │
└─────────────────────────────────────────────────────────────────────────────────┘

Operator SPIRE Agent Keycloak Target
│ │ │ │
│ 1. Watch workload │ │ │
│ 2. Register client │ │ │
├─────────────────────────┼────────────────────►│ │
│ │ │ │
│ Create credentials │ │ │
│ secret in agent ns │ │ │
│ │ │ │
│ ┌─────────┐│ 3. Get SVID │ │
│ │ SPIFFE ││◄────────────────────┤ │
│ │ Helper ││ │ │
│ └─────────┘│ │ │
│ │ │ │ │
│ ┌─────────┐│ │ │
│ │ Agent ││ 4. Get token │ │
│ │ ││────────────────────►│ │
│ │ ││◄────────────────────│ │
│ │ ││ (aud: agent's ID) │ │
│ │ ││ │ │
│ │ ││ 5. Request + token │ │
│ │ ││─────────────────────┼───────────────────►│
│ └─────────┘│ │ │
│ │ │ │ │
│ ┌──────────────┐│ │ │
│ │ Envoy+ext-pr ││ 6. Validate & │ │
│ │ ││ Exchange token │ │
│ │ ││────────────────────►│ │
│ │ ││◄────────────────────│ │
│ │ ││ (aud: target) │ │
│ │ ││─────────────────────┼───────────────────►│
│ └──────────────┘│ │ 7. Validate aud │
│ │ │ "authorized" │

AuthBridge Components

ComponentTypePurpose
Rossoctl OperatorControllerReconciles AgentRuntime CRs, applies rossoctl.io/type labels to target workloads, registers them as OAuth clients in Keycloak, and creates credentials secrets in agent namespaces
Envoy + Go Processor (Ext Proc)SidecarIntercepts traffic in both directions: inbound — validates JWT (signature, expiration, issuer, optional audience) via JWKS, returns 401 for invalid tokens; outbound — exchanges tokens for target audience via Keycloak

Note: Client registration is fully automatic. The operator reads Keycloak admin credentials from keycloak-admin-secret in the operator namespace (not from agent namespaces), providing better security isolation.

Hands-On Demos

For step-by-step AuthBridge demos with real working examples, see:

AuthBridge Documentation

For complete documentation, see:

Note: The AuthBridge demo in cortex includes client-registration components for demonstration purposes. In production Rossoctl deployments, client registration is handled by the rossoctl-operator controller.


📋 Quick Reference

Essential URLs

Default Credentials

# Keycloak Admin — run ./.github/scripts/local-setup/show-services.sh for actual credentials

Common SPIFFE IDs

Using local kind:

# Agents
spiffe://localtest.me/ns/team/sa/slack-researcher
spiffe://localtest.me/ns/team/sa/weather-service
spiffe://localtest.me/ns/team/sa/github-issue-agent

# Tools
spiffe://localtest.me/ns/team/sa/slack-tool
spiffe://localtest.me/ns/team/sa/weather-tool
spiffe://localtest.me/ns/team/sa/github-tool

# Infrastructure
spiffe://localtest.me/ns/gateway-system/sa/mcp-gateway
spiffe://localtest.me/ns/rossoctl-system/sa/operator

Using OpenShift:

# Agents
spiffe://apps.cluster-swkz5.dynamic.redhatworkshops.io/ns/team/sa/slack-researcher
spiffe://apps.cluster-swkz5.dynamic.redhatworkshops.io/ns/team/sa/weather-service
spiffe://apps.cluster-swkz5.dynamic.redhatworkshops.io/ns/team/sa/github-issue-agent

Token Exchange Endpoints

# Keycloak Token Endpoint
POST http://keycloak.keycloak.svc.cluster.local:8080/realms/rossoctl/protocol/openid-connect/token

# User Info Endpoint
GET http://keycloak.keycloak.svc.cluster.local:8080/realms/rossoctl/protocol/openid-connect/userinfo

# Token Introspection
POST http://keycloak.keycloak.svc.cluster.local:8080/realms/rossoctl/protocol/openid-connect/token/introspect

🔄 OAuth2 Token Exchange Flows

Token Exchange Protocol (RFC 8693)

Rossoctl implements OAuth2 Token Exchange to enable secure token delegation across the agent ecosystem. This allows:

  • User identity propagation through agent → tool chains
  • Least-privilege token scoping
  • Audit trails for all access requests

Authentication Flow Stages

📊 Diagrams: The following sequence diagrams illustrate Rossoctl's authentication flows. Each diagram is available as both PNG (for documentation) and SVG (for presentations) in the docs/diagrams/images/ directory. The original Mermaid source files are preserved in collapsible sections below each diagram.

Stage 1: User Authentication

User Authentication Flow

Figure 1: User Authentication Flow - Shows how users authenticate with Rossoctl UI through Keycloak OIDC flow

View Mermaid Source Code

HTTP Request:

POST /realms/rossoctl/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&client_id=rossoctl-ui
&code=<auth_code>
&redirect_uri=http://rossoctl-ui.localtest.me:8080/callback

Response:

{
"access_token": "eyJ0eXAiOiJKV1Q...",
"expires_in": 600,
"scope": "openid profile email",
"token_type": "Bearer",
"id_token": "eyJ0eXAiOiJKV1Q..."
}

Stage 2: Keycloak Client Registration Flow (Operator-managed)

Keycloak client registration is now handled by the rossoctl-operator's ClientRegistrationReconciler controller. The controller:

  1. Reconciles AgentRuntime CRs and applies rossoctl.io/type: agent or tool labels to target workloads
  2. Reads Keycloak admin credentials from the keycloak-admin-secret in the operator namespace (rossoctl-system)
  3. Uses the workload's SPIFFE ID as the client identifier
  4. Registers the client with Keycloak and creates a secret containing client credentials in the agent namespace

This approach provides better security isolation by restricting Keycloak admin credentials to the operator namespace rather than replicating them to every agent namespace.

Stage 3: Agent Token Exchange

Agent Token Exchange Flow

Figure 4: Agent Token Exchange Flow - Demonstrates OAuth2 token exchange between agents and Keycloak using SPIFFE identity

View Mermaid Source Code

Token Exchange Request:

POST /realms/rossoctl/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer <JWT-SVID-AGENT>

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=<USER-JWT-TOKEN>
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=slack-tool
&client_id=spiffe://localtest.me/ns/team/sa/slack-researcher

Token Exchange Response:

{
"access_token": "eyJ0eXAiOiJKV1Q...",
"expires_in": 300,
"scope": "slack-partial-access",
"token_type": "Bearer"
}

Stage 4: Internal Tool Access with Delegated Token

Internal Tool Access with Delegated Token Flow

Figure 4: Internal Tool Access Flow - Shows how agents call internal tools using delegated tokens with proper permission validation

View Mermaid Source Code

JWT Token Structure

User Token:

{
"sub": "user-123",
"preferred_username": "slack-full-access-user",
"aud": "rossoctl-ui",
"exp": 1735689600,
"roles": ["slack-full-access", "slack-partial-access"]
}

Agent-Scoped Token (after exchange):

{
"sub": "user-123",
"act": {
"sub": "spiffe://localtest.me/ns/team/sa/slack-researcher"
},
"aud": "slack-tool",
"exp": 1735686900,
"scope": "slack-full-access"
}

🌐 MCP Protocol Authentication

MCP Gateway Authentication

The MCP Gateway acts as an authentication proxy for all Model Context Protocol communications:

Stage 5: Gateway Authentication Flow

MCP Gateway Authentication Flow

Figure 5: MCP Gateway Authentication Flow - Illustrates authentication flow through the MCP Gateway proxy for Model Context Protocol communications

View Mermaid Source Code

MCP Authentication Headers

# Agent to Gateway
POST /mcp
Host: mcp-gateway.localtest.me:8080
Authorization: Bearer <JWT-TOKEN>
Content-Type: application/json

{
"method": "tools/list",
"params": {}
}

Gateway Configuration

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: slack-tool-route
labels:
mcp-server: "true"
spec:
parentRefs:
- name: mcp-gateway
namespace: gateway-system
hostnames:
- "slack-tool.mcp.local"
rules:
- matches:
- path:
type: PathPrefix
value: /
filters:
- type: ExtensionRef
extensionRef:
group: rossoctl.dev
kind: AuthFilter
name: jwt-validator
backendRefs:
- name: slack-tool-mcp
port: 8000

Tool-Specific Authentication

Slack Tool Authentication

# In Slack MCP Tool
def validate_request(request):
token = request.headers.get("Authorization", "").replace("Bearer ", "")

# Validate with Keycloak
response = requests.get(
"http://keycloak.keycloak.svc.cluster.local:8080/realms/rossoctl/protocol/openid-connect/userinfo",
headers={"Authorization": f"Bearer {token}"}
)

if response.status_code != 200:
raise AuthenticationError("Invalid token")

user_info = response.json()
scopes = user_info.get("scope", "").split()

# Check permissions
if "slack-full-access" in scopes:
return PermissionLevel.FULL
elif "slack-partial-access" in scopes:
return PermissionLevel.PARTIAL
else:
raise AuthorizationError("Insufficient permissions")

Stage 6: External API Access with Delegated Token and Vault

External API Access with Delegated Token and Vault Flow

Figure 6: External API Access with Vault Flow - Shows how agents call internal tools using delegated tokens with proper permission validation and the Vault exchanges this token for external API key for accessing external APIs

View Mermaid Source Code

JWT Token Structure

User Token:

{
"sub": "user-123",
"preferred_username": "slack-full-access-user",
"aud": "rossoctl-ui",
"exp": 1735689600,
"roles": ["slack-full-access", "slack-partial-access"]
}

Agent-Scoped Token (after exchange):

{
"sub": "user-123",
"act": {
"sub": "spiffe://localtest.me/ns/team/sa/slack-researcher"
},
"aud": "slack-tool",
"exp": 1735686900,
"scope": "slack-full-access"
}

📚 Additional Resources

Standards and Specifications

Implementation Guides

Community Resources