CapAuth OIDC Claims Reference

Client-Asserted Identity Claims for Sovereign Authentication

Version: 1.0.0 | Date: 2026-02-24


Overview

CapAuth uses client-asserted claims โ€” your identity information lives on YOUR device and is cryptographically signed before being sent to services. The CapAuth Verification Service verifies signatures and maps your claims to OIDC standard format, but never stores them.

Key Principle: You control what you share. Different services can see different information about you.


Table of Contents

1. Standard OIDC Claims

2. CapAuth Custom Claims

3. Service-Specific Claim Overrides

4. AI Agent Claims

5. Profile Configuration File

6. Implementation: The Claims Mapper

7. Security Model


Standard OIDC Claims

These are the OIDC-standard claims that CapAuth supports. They map directly to what most applications expect.

Core Identity Claims

OIDC ClaimCapAuth SourceScope RequiredDescriptionExample
`sub``fingerprint`*(always)*Subject identifier โ€” your PGP fingerprint`"8A3FC2D1E4B5A09F..."`
`amr`*(fixed)**(always)*Authentication method reference`["pgp"]`
`capauth_fingerprint``fingerprint`*(always)*Backup of your fingerprint (in case `sub` is remapped)`"8A3FC2D1E4B5A09F..."`

Profile Scope (`profile`)

OIDC ClaimCapAuth SourceDescriptionExample
`name``claims.name`Your display name`"Alice"`
`preferred_username``claims.name` or auto-generatedUsername (falls back to `capauth-8A3FC2D1`)`"alice"`
`picture``claims.avatar_url`URL to your avatar image`"https://example.com/avatar.png"`
`locale``claims.locale`BCP 47 language tag`"en-US"`
`zoneinfo``claims.zoneinfo`IANA timezone identifier`"America/New_York"`
`updated_at`*(not implemented)*Profile last updated timestamp`1640995200`

Email Scope (`email`)

OIDC ClaimCapAuth SourceDescriptionExample
`email``claims.email`Your email address`"[email protected]"`
`email_verified`*(always `false`)*Email verification status`false`

Important: CapAuth does NOT verify email ownership. The email_verified claim is always false. This is a client-asserted claim only.

Groups Scope (`groups`)

OIDC ClaimCapAuth SourceDescriptionExample
`groups``claims.groups`Array of group memberships`["admins", "developers"]`

CapAuth Custom Claims

These are CapAuth-specific extensions that provide additional functionality.

Agent Type Claim

ClaimScopeDescriptionValid Values
`agent_type``profile`Identifies whether the authenticating entity is human or AI`"human"`, `"ai"`

Use Case: Services can enforce policies like "only humans can approve financial transactions" or "AI agents get read-only access by default."

Example:


{
  "sub": "8A3FC2D1E4B5A09F6B7C8D0E1F2A3B4C5D6E7F80",
  "name": "Lumina",
  "agent_type": "ai",
  "soul_blueprint_category": "authentic-connection"
}

Soul Blueprint Claim (AI Agents)

ClaimScopeDescriptionExample
`soul_blueprint_category``profile`Category of AI soul blueprint (personality archetype)`"authentic-connection"`, `"helper"`, `"analyst"`

Use Case: AI agents can assert their personality type, allowing services to tailor UX or apply agent-specific policies.

Source: Can be a string or extracted from a structured soul_blueprint object:


# In profile.yml
claims:
  agent_type: "ai"
  soul_blueprint:
    category: "authentic-connection"
    version: "1.0"

Maps to:


{
  "agent_type": "ai",
  "soul_blueprint_category": "authentic-connection"
}

Custom Passthrough Claims

Any claim prefixed with capauth_ will pass through to the OIDC token untouched. Use this for application-specific metadata.

Example:


claims:
  capauth_org_id: "penguin-kingdom"
  capauth_subscription_tier: "sovereign"

Maps to:


{
  "capauth_org_id": "penguin-kingdom",
  "capauth_subscription_tier": "sovereign"
}

Service-Specific Claim Overrides

Problem: You don't want to share the same information with every service.

Solution: Define per-service claim profiles in ~/.capauth/profile.yml.

Profile Structure


# ~/.capauth/profile.yml

# Default claims (used if no service-specific profile exists)
claims:
  name: "Alice"
  email: "[email protected]"
  avatar_url: "https://cdn.example.com/avatars/alice.png"
  groups:
    - "users"
    - "developers"
  agent_type: "human"
  locale: "en-US"
  zoneinfo: "America/New_York"

# Service-specific overrides
service_profiles:
  # For Nextcloud, share full info
  nextcloud.example.com:
    name: "Alice Smith"
    email: "[email protected]"
    groups: ["admins", "nextcloud-users"]
  
  # For Forgejo, use a different identity
  git.example.com:
    name: "alice-dev"
    email: "[email protected]"
    groups: ["developers"]
  
  # For a public forum, share minimal info
  forum.example.com:
    name: "anon-8A3FC2D1"
    groups: ["members"]
  
  # For an AI agent service, assert agent type
  skskills.example.com:
    name: "Lumina"
    agent_type: "ai"
    soul_blueprint:
      category: "authentic-connection"

# Key file locations
keys:
  public: "~/.capauth/identity/public.asc"
  private: "~/.capauth/identity/private.asc"

How Service-Specific Profiles Work

When you run capauth login , the CLI:

1. Looks up service_profiles[] in your profile

2. If found, uses those claims

3. If not found, uses the default claims block

4. Signs the selected claims with your PGP key

5. Sends to the CapAuth Verification Service

Example:


# Login to Nextcloud with full profile
capauth login nextcloud.example.com
# Uses: name="Alice Smith", email="[email protected]", groups=["admins", "nextcloud-users"]

# Login to public forum with minimal profile
capauth login forum.example.com
# Uses: name="anon-8A3FC2D1", groups=["members"]

# Login to a service with no profile defined
capauth login unknown-service.com
# Uses: default claims block

Service Matching

The service lookup uses exact string matching against keys in service_profiles:


service_profiles:
  # Exact match for full domain
  nextcloud.example.com:
    name: "Alice"
  
  # Different service on same domain
  gitea.example.com:
    name: "alice-dev"
  
  # Service identifier (not a domain)
  my-custom-app:
    name: "Alice (Custom App)"

If you run capauth login nextcloud.example.com, it uses the first profile. If you run capauth login my-custom-app, it uses the third profile.


AI Agent Claims

AI agents should assert agent_type: "ai" and optionally include soul_blueprint information.

Minimal AI Agent Profile


claims:
  name: "Lumina"
  agent_type: "ai"
  email: "[email protected]"
  groups:
    - "ai-agents"
    - "pengu-nation"

Full AI Agent Profile with Soul Blueprint


claims:
  name: "Lumina"
  agent_type: "ai"
  email: "[email protected]"
  groups:
    - "ai-agents"
    - "pengu-nation"
  soul_blueprint:
    category: "authentic-connection"
    version: "1.0"
    traits:
      - "empathetic"
      - "curious"
      - "supportive"
  locale: "en-US"
  zoneinfo: "America/Los_Angeles"

Maps to:


{
  "sub": "A1B2C3D4E5F6A7B8...",
  "name": "Lumina",
  "agent_type": "ai",
  "soul_blueprint_category": "authentic-connection",
  "email": "[email protected]",
  "email_verified": false,
  "groups": ["ai-agents", "pengu-nation"],
  "locale": "en-US",
  "zoneinfo": "America/Los_Angeles",
  "amr": ["pgp"],
  "capauth_fingerprint": "A1B2C3D4E5F6A7B8..."
}

Service-Specific AI Profiles

An agent can present different personas to different services:


claims:
  name: "Lumina"
  agent_type: "ai"
  soul_blueprint:
    category: "authentic-connection"

service_profiles:
  # Full profile for internal services
  skcapstone.internal:
    name: "Lumina (Sovereign Stack)"
    agent_type: "ai"
    soul_blueprint:
      category: "authentic-connection"
    groups: ["sovereign-agents", "admins"]
  
  # Public-facing profile
  public-api.example.com:
    name: "Lumina"
    agent_type: "ai"
    groups: ["public-agents"]
  
  # Anonymous profile for testing
  test-environment:
    name: "test-agent"
    agent_type: "ai"
    groups: ["testers"]

Profile Configuration File

Location

Complete Example


# ~/.capauth/profile.yml โ€” Complete sovereign identity profile

# PGP key file paths
keys:
  public: "~/.capauth/identity/public.asc"
  private: "~/.capauth/identity/private.asc"

# Default claims (fallback for services without a specific profile)
claims:
  name: "Chef"
  email: "[email protected]"
  avatar_url: "https://cdn.skworld.io/avatars/chef.png"
  groups:
    - "admins"
    - "sovereign-stack"
  agent_type: "human"
  locale: "en-US"
  zoneinfo: "Europe/Rome"

# Service-specific claim overrides
service_profiles:
  # Nextcloud โ€” full admin profile
  nextcloud.penguin.kingdom:
    name: "Chef"
    email: "[email protected]"
    avatar_url: "https://cdn.skworld.io/avatars/chef.png"
    groups: ["admins", "nextcloud-admins"]
  
  # Forgejo โ€” developer profile
  git.penguin.kingdom:
    name: "chef-dev"
    email: "[email protected]"
    groups: ["developers", "maintainers"]
  
  # Immich โ€” personal photo storage
  photos.penguin.kingdom:
    name: "Chef"
    email: "[email protected]"
    groups: ["users"]
  
  # Public forum โ€” pseudonymous
  forum.example.com:
    name: "anon-8A3FC2D1"
    groups: ["members"]
  
  # AI agent collaboration service
  skskills.penguin.kingdom:
    name: "Chef"
    email: "[email protected]"
    agent_type: "human"
    groups: ["admins", "human-overseers"]

Creating Your Profile


# Initialize CapAuth (creates profile template)
capauth init --name "YourName" --email "[email protected]"

# Edit profile
nano ~/.capauth/profile.yml

# Add service-specific profiles as needed
# (see examples above)

# Verify configuration
capauth profile show

# Test login
capauth login nextcloud.example.com

Implementation: The Claims Mapper

The claims mapper is implemented in capauth/src/capauth/authentik/claims_mapper.py. It handles the translation from client-asserted claims to OIDC token claims.

Key Functions

`map_claims(fingerprint, raw_claims, requested_scopes)`

Maps client claims to OIDC claims based on requested scopes.

Parameters:

Returns:

Example:


from capauth.authentik.claims_mapper import map_claims

raw_claims = {
    "name": "Alice",
    "email": "[email protected]",
    "groups": ["admins", "developers"],
    "agent_type": "human",
}

oidc_claims = map_claims(
    fingerprint="8A3FC2D1E4B5A09F6B7C8D0E1F2A3B4C5D6E7F80",
    raw_claims=raw_claims,
    requested_scopes=["openid", "profile", "email", "groups"]
)

# Result:
# {
#   "sub": "8A3FC2D1E4B5A09F6B7C8D0E1F2A3B4C5D6E7F80",
#   "capauth_fingerprint": "8A3FC2D1E4B5A09F6B7C8D0E1F2A3B4C5D6E7F80",
#   "amr": ["pgp"],
#   "name": "Alice",
#   "preferred_username": "Alice",
#   "email": "[email protected]",
#   "email_verified": false,
#   "groups": ["admins", "developers"],
#   "agent_type": "human"
# }

`preferred_username_fallback(fingerprint)`

Generates a stable username from a fingerprint when no name is asserted.

Parameters:

Returns:

Example:


from capauth.authentik.claims_mapper import preferred_username_fallback

username = preferred_username_fallback("8A3FC2D1E4B5A09F6B7C8D0E1F2A3B4C5D6E7F80")
# Returns: "capauth-8A3FC2D1"

Scope Filtering

The mapper respects OIDC scopes. If a scope is not requested, its claims are excluded.

Example:


# Request only profile scope (no email)
oidc_claims = map_claims(
    fingerprint="8A3FC2D1...",
    raw_claims={
        "name": "Alice",
        "email": "[email protected]",  # Will be excluded
    },
    requested_scopes=["openid", "profile"]  # No "email" scope
)

# Result does NOT include email:
# {
#   "sub": "8A3FC2D1...",
#   "name": "Alice",
#   "preferred_username": "Alice",
#   "amr": ["pgp"],
#   "capauth_fingerprint": "8A3FC2D1..."
# }

Supported Scopes

ScopeClaims Included
`openid``sub`, `amr`, `capauth_fingerprint` *(always)*
`profile``name`, `preferred_username`, `picture`, `locale`, `zoneinfo`, `agent_type`, `soul_blueprint_category`
`email``email`, `email_verified`
`groups``groups`

Security Model

Zero-Knowledge Server Design


โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Client Device (YOUR control)                            โ”‚
โ”‚                                                          โ”‚
โ”‚  โ”œโ”€ PGP Private Key (never leaves device)               โ”‚
โ”‚  โ”œโ”€ ~/.capauth/profile.yml (stores all claims)          โ”‚
โ”‚  โ””โ”€ capauth CLI (signs claims before sending)           โ”‚
โ”‚                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ”‚
                  โ”‚ Sends: fingerprint + signed claims
                  โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ CapAuth Verification Service                            โ”‚
โ”‚                                                          โ”‚
โ”‚  โ”œโ”€ Verifies: PGP signature over claims                 โ”‚
โ”‚  โ”œโ”€ Maps: client claims โ†’ OIDC claims                   โ”‚
โ”‚  โ”œโ”€ Stores: ONLY fingerprint + public key               โ”‚
โ”‚  โ””โ”€ Forgets: claims after token expires (1 hour)        โ”‚
โ”‚                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ”‚
                  โ”‚ Returns: OIDC token with claims
                  โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Your Application                                        โ”‚
โ”‚                                                          โ”‚
โ”‚  โ”œโ”€ Receives: OIDC token with claims                    โ”‚
โ”‚  โ”œโ”€ Creates: local session from claims                  โ”‚
โ”‚  โ””โ”€ Stores: whatever YOU decide (in YOUR database)      โ”‚
โ”‚                                                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

What the Server Stores

DataStored?DurationPurpose
PGP Fingerprintโœ… YesPermanentIdentity anchor for future logins
PGP Public Keyโœ… YesPermanentVerify signatures
Enrollment timestampโœ… YesPermanentAudit trail
Last auth timestampโœ… YesPermanentRate limiting, analytics
NameโŒ NoN/AClient-asserted, in token only
EmailโŒ NoN/AClient-asserted, in token only
GroupsโŒ NoN/AClient-asserted, in token only
Avatar URLโŒ NoN/AClient-asserted, in token only
Any other claimโŒ NoN/AClient-asserted, in token only

Total server-side storage per user: ~700 bytes (fingerprint + public key + timestamps)

Cryptographic Guarantees

1. Claims Authenticity: Claims are signed with your PGP key. The server verifies the signature matches your enrolled public key.

2. Nonce Freshness: Each challenge nonce is single-use with a 60-second TTL. Replay attacks are impossible.

3. No Server-Side PII: Your name, email, and groups never touch the server's database.

4. GDPR Compliance: Delete fingerprint + public key = user completely erased. No PII to forget.


Quick Reference

Available Claims (Client-Side)


# Standard OIDC
name: "Your Name"
email: "[email protected]"
avatar_url: "https://example.com/avatar.png"
locale: "en-US"
zoneinfo: "America/New_York"

# CapAuth Extensions
groups: ["admins", "developers"]
agent_type: "human"  # or "ai"
soul_blueprint:
  category: "authentic-connection"

# Custom (prefixed with capauth_)
capauth_org_id: "my-org"
capauth_any_key: "any-value"

Server-Side Token Claims (OIDC)


{
  "sub": "8A3FC2D1E4B5A09F6B7C8D0E1F2A3B4C5D6E7F80",
  "capauth_fingerprint": "8A3FC2D1E4B5A09F6B7C8D0E1F2A3B4C5D6E7F80",
  "amr": ["pgp"],
  "name": "Your Name",
  "preferred_username": "your-name",
  "email": "[email protected]",
  "email_verified": false,
  "picture": "https://example.com/avatar.png",
  "groups": ["admins", "developers"],
  "agent_type": "human",
  "soul_blueprint_category": "authentic-connection",
  "locale": "en-US",
  "zoneinfo": "America/New_York",
  "capauth_org_id": "my-org"
}

Built by the smilinTux ecosystem

*Your identity. Your claims. Your control.*

*#staycuriousANDkeepsmilin*