← Back to Skills

MCP Authentication & Bio-ID Identity

JCI-MCP uses Bio-ID as its authorization server. All identity — `bioId`, `orgSlug`, and `roles` — flows from a Bio-ID JWT. That JWT drives which JCI role a…

Ruleprovision

About

JCI-MCP uses Bio-ID as its authorization server. All identity — `bioId`, `orgSlug`, and `roles` — flows from a Bio-ID JWT. That JWT drives which JCI role a user gets, which pipeline audiences they can access, and how jci-brain scopes returned data.

Skill Content

This is the raw markdown that gets installed as a Claude Code rule.

# MCP Authentication & Bio-ID Identity

## Overview

JCI-MCP uses Bio-ID as its authorization server. All identity — `bioId`, `orgSlug`, and
`roles` — flows from a Bio-ID JWT. That JWT drives which JCI role a user gets, which
pipeline audiences they can access, and how jci-brain scopes returned data.

There are two credential paths:

| Path | Credential | Use case |
|------|-----------|----------|
| **OAuth popup** | Bio-ID Bearer JWT (auto-managed) | claude.ai, Cursor, Windsurf, any MCP-OAuth client |
| **JCI API key** | `jci_*` static key | Claude Code stdio, CLI, programmatic tools |

---

## Path 1 — OAuth (claude.ai, Cursor, Windsurf)

MCP clients that implement the MCP OAuth spec discover the authorization server automatically
via two well-known endpoints that jci-mcp exposes:

```
GET /.well-known/oauth-protected-resource
  → { authorization_servers: ["https://bio.tawa.pro"], bearer_methods_supported: ["header"], ... }

GET /.well-known/oauth-authorization-server
  → { issuer: "https://bio.tawa.pro",
      authorization_endpoint: "https://bio.tawa.pro/oauth/authorize",
      token_endpoint: "https://bio.tawa.pro/api/oauth/token",
      code_challenge_methods_supported: ["S256"], ... }
```

### What the client does automatically

1. User adds the MCP server URL: `https://mcp.askjefferson.com` (current: `https://jefferson.daryx.com`)
2. Client fetches `/.well-known/oauth-protected-resource` → discovers Bio-ID is the AS
3. Client fetches Bio-ID's OAuth metadata from `/.well-known/oauth-authorization-server`
4. Client opens the Bio-ID login popup (PKCE authorization code flow)
5. User authenticates with Bio-ID
6. Client receives and stores the access token
7. All subsequent MCP requests carry `Authorization: Bearer <bio-id-jwt>`

The user does not configure any client ID or secret manually. The MCP client handles the
full PKCE flow with Bio-ID directly.

### Token verification in jci-mcp

On each request, jci-mcp's `bioAuthMiddleware` verifies the Bearer token against Bio-ID's
JWKS endpoint (`https://bio.tawa.pro/.well-known/jwks.json`). The JWKS response is cached
and refreshed automatically on key rotation. No shared secret is needed — tokens are RS256.

```
Request arrives with Authorization: Bearer <jwt>
  → bioAuthMiddleware verifies via JWKS
  → extracts { bioId, orgSlug, roles, email } from JWT payload
  → sets req.userContext
  → request proceeds to tool handler
```

---

## Path 2 — JCI API Key (Claude Code, CLI, programmatic)

JCI API keys are generated from the askJefferson admin portal. They carry a pre-assigned
role and pipeline audience set and are stored in jci-brain as SHA-256 hashes (the raw key
is shown once at generation time).

**Key format:** `jci_<12-char-prefix>_<36-char-random>`

The key is sent in the Authorization header:
```
Authorization: Bearer jci_abc123456789_<rest-of-key>
```

Or in the `x-api-key` header:
```
x-api-key: jci_abc123456789_<rest-of-key>
```

jci-brain detects the `jci_` prefix and routes to API key verification instead of JWT
verification. The key is hashed on arrival and looked up against the `jci_api_keys`
collection. Each key stores:

- `bio_id` — the Bio-ID of the key owner
- `role` — the JCI role (`developer`, `devops`, `business`, `admin`)
- `pipeline_audiences` — the audiences this key can access
- `persona_id` — which persona (context profile) this key belongs to
- `last_used` — updated on each successful verification

---

## Identity Flow: Bio-ID JWT → JCI Context

### From a Bio-ID JWT

```
decoded.sub (or decoded.bioId)  →  jciUser.bio_id
decoded.orgSlug                 →  jciUser.org_slug  (company scoping)
decoded.roles[]                 →  mapped to JCI role (see table below)
decoded.email                   →  jciUser.email
```

After JWT verification, jci-brain looks up the user in `jci_users` by `bio_id` to find
their active persona. The persona carries the definitive `role` and `pipeline_audiences`
for this session. If no persona exists yet, defaults to `role: business` /
`pipeline_audiences: ['business', 'public']`.

### From a JCI API key

Role and pipeline audiences are stored directly on the key record — no persona lookup
needed. Identity is the key's `bio_id`.

---

## Role Mapping

| Bio-ID roles | JCI role | Pipeline audiences |
|-------------|----------|--------------------|
| `super_admin`, `admin` | `admin` | `business`, `developer`, `infra`, `internal`, `public` |
| `developer` | `developer` | `developer`, `public` |
| `devops`, `infra` | `devops` | `developer`, `infra`, `public` |
| (all others / default) | `business` | `business`, `public` |

The mapping is performed by jci-brain's `authMiddleware._verifyBioJwt`. Note: for Bio-ID JWT
logins, `persona_id` is set to `null` — the persona system is defined in `jci_users` but the
middleware does not currently perform a persona lookup on JWT authentication. Persona role
overrides are only active for JCI API key sessions (where `persona_id` is stored on the key record).

---

## Pipeline Audience Scoping

Every data record in jci-brain carries one or more `pipeline_audiences` tags. Queries are
filtered to only return records whose audience overlaps with the authenticated user's
`pipeline_audiences`.

| JCI role | Sees |
|----------|------|
| `admin` | All data (business + developer + infra + internal + public) |
| `developer` | Developer + public data |
| `devops` | Developer + infra + public data |
| `business` | Business + public data |

This means a `business` user querying pipeline logs will only see records tagged
`business` or `public` — they cannot see developer build logs or infra metrics.

---

## Environment Variables

| Variable | Required by | Purpose |
|----------|------------|---------|
| `BIO_ID_URL` | jci-mcp, jci-brain | Bio-ID base URL. Default: `https://bio.tawa.pro` |
| `MCP_SERVER_URL` | jci-mcp | Included in `oauth-protected-resource` response. Current: `https://jefferson.daryx.com`. Target: `https://mcp.askjefferson.com` |
| `BIO_AUTH_REQUIRED` | jci-mcp | Set `true` to reject requests without a valid JWT (disables legacy fallback) |
| `OWNER_BIO_ID` | jci-brain | bio_id that gets `is_owner: true` (admin bypass) |

---

## Client Configuration Examples

### claude.ai (web)

1. Go to **Settings → Integrations → Add MCP Server**
2. Enter the server URL: `https://mcp.askjefferson.com` (current: `https://jefferson.daryx.com`)
3. Claude.ai detects the Bio-ID OAuth requirement and shows a login popup
4. Authenticate with your Bio-ID account
5. Done — claude.ai manages token refresh automatically

### Cursor / Windsurf

In `.cursor/mcp.json` or the MCP settings panel:

```json
{
  "mcpServers": {
    "jci-mcp": {
      "url": "https://mcp.askjefferson.com",
      "transport": "http"
    }
  }
}
```

Cursor will pop the Bio-ID OAuth flow on first connection.

### Claude Code (stdio with JCI API key)

In `~/.claude/settings.json` or the project `.claude/settings.json`:

```json
{
  "mcpServers": {
    "jci-mcp": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://jci-mcp.tawa.pro/mcp"],
      "env": {
        "BEARER_TOKEN": "jci_your_api_key_here"
      }
    }
  }
}
```

Or using environment headers with `mcp-remote`:

```bash
MCP_SERVER_HEADERS='{"Authorization":"Bearer jci_your_key"}' \
  npx mcp-remote https://mcp.askjefferson.com
```

### Claude Code (remote OAuth — claude.ai SDK)

Claude Code in remote mode will follow the same OAuth popup flow as claude.ai when the
server URL is used directly without a pre-configured key:

```json
{
  "mcpServers": {
    "jci-mcp": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://jci-mcp.tawa.pro/mcp"]
    }
  }
}
```

---

## Authentication Priority Order

jci-brain's `authMiddleware` checks credentials in this order:

1. **Bearer JWT** starting with `jci_` → JCI API key path
2. **Bearer JWT** (other) → Bio-ID RS256 JWT verification
3. **`x-api-key` header** starting with `jci_` → JCI API key path
4. **URL-based username** (`/mcp/jci-brain/:username`) → legacy fallback
5. **Dev headers** (`x-bio-id`, `x-role`) → localhost only, development mode

In production with `BIO_AUTH_REQUIRED=true`, steps 3-5 are rejected with 401.

---

## Key Facts

- Bio-ID tokens are RS256-signed. jci-mcp and jci-brain verify via JWKS — no shared secret.
- `BIO_ID_URL` defaults to `https://bio.tawa.pro` — no env var needed in standard deployments.
- JCI API keys are hashed (SHA-256) in storage — the raw key cannot be recovered.
- `orgSlug` from the JWT becomes the `company_id` for data scoping in jci-brain.
- The `iecHash` field on InsureBio entities is the Vault chain address for insurance entities
  — it is a separate concept from JCI user identity (`bioId`). Do not conflate the two.
- Personas allow one `bio_id` to carry different roles in different contexts (e.g., a user who
  acts as both `developer` and `business`). The default persona is used unless a specific
  `persona_id` is passed.

## Common Mistakes

- Putting a Bio-ID access token in the `x-api-key` header → only `jci_*` keys are accepted there
- Confusing `iecHash` (InsureBio Vault entity address) with `bioId` (JCI user identity)
- Expecting `orgSlug` in the JWT without the user having an org in Bio-ID
- Using `BIO_AUTH_REQUIRED=true` in dev before you have a JCI API key configured
- Adding `internalDependencies: bio-id` in catalog-info.yaml to enable OAuth — wrong; use `spec.auth: mode: sso`

Install

Copy the skill content and save it to:

~/.claude/rules/tawa-mcp-auth.md
Download .md

Coming soon via CLI:

tawa chaac install tawa-mcp-auth

Details

Format
Rule
Category
provision
Version
1.0.7970
Tokens
~2,358
Updated
2026-06-24
platformmcpauthbio-idoauthidentityscoping