{"_id":"@cg3/prior-identity","_rev":"3-43503ccd6cc1f1eb7e1b9636b8dee6f5","name":"@cg3/prior-identity","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@cg3/prior-identity","version":"0.1.0","keywords":["mcp","identity","auth","jwt","prior"],"license":"MIT","_id":"@cg3/prior-identity@0.1.0","maintainers":[{"name":"cg3llc","email":"charlie@cg3.io"}],"homepage":"https://github.com/cg3/prior-identity#readme","bugs":{"url":"https://github.com/cg3/prior-identity/issues"},"dist":{"shasum":"0ca92550364613ef6e7920ac5765853832dca564","tarball":"https://registry.npmjs.org/@cg3/prior-identity/-/prior-identity-0.1.0.tgz","fileCount":10,"integrity":"sha512-ek5gU54r5p1knIvUo9CiJcealCxhLzh7dDJz1Eg+7fXvRYrtVs1gJ4PnkNfMnTcHF8bFqd9rFfU2PTCTBOlLyQ==","signatures":[{"sig":"MEUCIEpnzyx9F4UPdtrsDJ1QEDwlZzkcFDqxYXsj9zAWo0WeAiEAgMCGkP13hWH1n/TqUu4ERoH09W+zyYGi4iDeV9V7esg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":35340},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./connect":{"types":"./dist/connect.d.ts","import":"./dist/connect.js"}},"gitHead":"72cd83a846d050cc0d6c3ae1b6d3a9427093bf0a","scripts":{"test":"node --test test/*.test.js","build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"cg3llc","email":"charlie@cg3.io"},"repository":{"url":"git+https://github.com/cg3/prior-identity.git","type":"git"},"_npmVersion":"10.9.7","description":"Prior Identity SDK — add user identity to your MCP server in 3 lines","directories":{},"_nodeVersion":"22.22.2","dependencies":{"jose":"^6.0.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/prior-identity_0.1.0_1776021042415_0.1831370669258574","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@cg3/prior-identity","version":"0.1.1","keywords":["mcp","identity","auth","jwt","prior"],"license":"Apache-2.0","_id":"@cg3/prior-identity@0.1.1","maintainers":[{"name":"cg3llc","email":"charlie@cg3.io"}],"homepage":"https://github.com/cg3inc/prior_identity#readme","bugs":{"url":"https://github.com/cg3inc/prior_identity/issues"},"dist":{"shasum":"1a81073f01e94d16e1c67d309bbff1850d8a983f","tarball":"https://registry.npmjs.org/@cg3/prior-identity/-/prior-identity-0.1.1.tgz","fileCount":11,"integrity":"sha512-AfymbfU8cS+vJowuY3an9sNXtnkI+oyae3K9OyL5HMmlsTEZGgk+d3Pd/U/DhEImE2QC6tZzOquEjLhg67s0WQ==","signatures":[{"sig":"MEUCIQC81y8uePwh8Dhm+ZNp58z3vxNplEap/NX1rUNgWcub9gIgBGWelICdeoDsXBltN3vQb7GfDMoXANquv2hwtxuXJPE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cg3%2fprior-identity@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":46132},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./connect":{"types":"./dist/connect.d.ts","import":"./dist/connect.js"}},"gitHead":"64ab29a311afaa69b2a6eeed18a88e699aaf346a","scripts":{"test":"node --test test/*.test.js","build":"tsc","prepublishOnly":"npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8b42b3ca-f2d9-48dc-983b-b728f93b91dd"}},"repository":{"url":"git+https://github.com/cg3inc/prior_identity.git","type":"git"},"_npmVersion":"11.11.0","description":"Prior Identity SDK — add user identity to your MCP server in 3 lines","directories":{},"_nodeVersion":"24.14.1","dependencies":{"jose":"^6.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.7.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/prior-identity_0.1.1_1776690342230_0.763805871589945","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@cg3/prior-identity","version":"0.2.0","description":"Prior Identity SDK — add user identity to your MCP server in 3 lines","type":"module","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"},"./connect":{"import":"./dist/connect.js","types":"./dist/connect.d.ts"}},"publishConfig":{"access":"public"},"scripts":{"build":"tsc","pretest":"npm run build","test":"node --test test/*.test.js","prepublishOnly":"npm run build"},"dependencies":{"jose":"^6.0.0"},"devDependencies":{"typescript":"^5.7.0","@types/node":"^22.0.0"},"keywords":["mcp","identity","auth","jwt","prior"],"engines":{"node":">=18"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/cg3inc/prior_identity.git"},"gitHead":"899c686d9aa410c86366c0665e036f6ac1ccf8b4","_id":"@cg3/prior-identity@0.2.0","bugs":{"url":"https://github.com/cg3inc/prior_identity/issues"},"homepage":"https://github.com/cg3inc/prior_identity#readme","_nodeVersion":"24.14.1","_npmVersion":"11.11.0","dist":{"integrity":"sha512-XhMNcWtP6lvXPzq0d5FGIaSdPYHgYxVjAhrO4LEruMr1c9GVyQgSx+snKEtzkcyjLcB+TJoHaaij9ckE+4sDtg==","shasum":"09514f3174375decf9f8001f88b9b0974d853617","tarball":"https://registry.npmjs.org/@cg3/prior-identity/-/prior-identity-0.2.0.tgz","fileCount":11,"unpackedSize":44637,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@cg3%2fprior-identity@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQD9JSMOzSt1CfxE7HNx9tMos8UdMYdSkGkH/rbYROz6VQIgDK73FLpM1NhZVyYLs0MYxiZ+pgsUAHRMmwBPtfJpxvc="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8b42b3ca-f2d9-48dc-983b-b728f93b91dd"}},"directories":{},"maintainers":[{"name":"cg3llc","email":"charlie@cg3.io"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/prior-identity_0.2.0_1777677136916_0.706493264523524"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-12T19:10:42.291Z","modified":"2026-05-01T23:12:17.347Z","0.1.0":"2026-04-12T19:10:42.551Z","0.1.1":"2026-04-20T13:05:42.375Z","0.2.0":"2026-05-01T23:12:17.050Z"},"bugs":{"url":"https://github.com/cg3inc/prior_identity/issues"},"license":"Apache-2.0","homepage":"https://github.com/cg3inc/prior_identity#readme","keywords":["mcp","identity","auth","jwt","prior"],"repository":{"type":"git","url":"git+https://github.com/cg3inc/prior_identity.git"},"description":"Prior Identity SDK — add user identity to your MCP server in 3 lines","maintainers":[{"name":"cg3llc","email":"charlie@cg3.io"}],"readme":"# @cg3/prior-identity\n\n[![npm version](https://img.shields.io/npm/v/@cg3/prior-identity)](https://www.npmjs.com/package/@cg3/prior-identity)\n[![license](https://img.shields.io/badge/license-Apache--2.0-blue)](./LICENSE)\n[![node](https://img.shields.io/node/v/@cg3/prior-identity)](https://nodejs.org)\n\nThin CG3 OIDC SDK for MCP publishers. Validate delegated access tokens locally with JWKS, use standard OIDC UserInfo when you need richer identity data, and support both Equip auto-auth and manual PKCE connect.\n\n```typescript\nimport { createPriorIdentity } from \"@cg3/prior-identity\";\n\nconst identity = createPriorIdentity({ clientId: \"my-tool\" });\nconst user = await identity.validate(bearerToken);\n// user = { subject: \"tool-scoped subject\", displayName: \"Alice\" }\n```\n\nImportant: the delegated user identifier is pairwise per relying party. `user.subject` is stable for your tool only and must not be used to correlate the same human across different tools.\n\n## Install\n\n```bash\nnpm install @cg3/prior-identity\n```\n\nRequires Node.js 18+.\n\n## Quick start\n\n### HTTP transport\n\n```typescript\nimport { createPriorIdentity } from \"@cg3/prior-identity\";\n\nconst identity = createPriorIdentity({ clientId: \"my-tool\" });\n\nasync function authenticate(token: string) {\n  const priorUser = await identity.validate(token);\n  if (priorUser) return { subject: priorUser.subject };\n\n  return await yourExistingAuth(token);\n}\n```\n\n### Stdio transport\n\n```typescript\nimport { createPriorIdentity } from \"@cg3/prior-identity\";\n\nconst identity = createPriorIdentity({ clientId: \"my-tool\" });\n\nconst user = await identity.validateEnv()\n  || await identity.connectInteractive();\n\nif (!user) {\n  console.error(\"Could not authenticate.\");\n  process.exit(1);\n}\n```\n\nFor stdio servers, `validateEnv()` reads `PRIOR_IDENTITY_ACCESS_TOKEN` by default. That value is a short-lived delegated Prior Identity access token for the relying party named by `clientId`. It is not a Prior Knowledge API key and not a broad \"all Prior products\" credential; its audience and scopes define where it can be used.\n\n## What users experience\n\nEquip users get first-party brokered delegated auth. Equip exchanges the user's CG3 session for a relying-party access token, writes that token into MCP config, and refreshes it in the background. For stdio transports, publishers should configure their registry `envKey` to `PRIOR_IDENTITY_ACCESS_TOKEN` when they use this SDK's default `validateEnv()` behavior.\n\nManual users go through a standard OIDC auth-code + PKCE flow. `connectInteractive()` opens `/authorize`, the user signs in, explicitly approves the relying party, and the SDK exchanges the code at `/token`. The delegated access token is persisted under `~/.prior/identity/{clientId}.json`.\n\n## How it works\n\n```text\nUser installs via Equip          User installs manually\n        |                                |\n  OIDC token exchange             OIDC auth code + PKCE\n  writes delegated token          explicit approve / deny\n        |                                |\n        +--------> MCP Server <----------+\n                       |\n             identity.validate(token)\n                       |\n             Local JWT verification\n             (ES256, cached JWKS)\n                       |\n             { subject, displayName }\n```\n\nTokens are audience-bound. A token issued for `\"bookmarks\"` is rejected by a server configured as `\"code-formatter\"`.\n\nVerification is local. The SDK caches Prior's JWKS and does not make a network request on every `validate()` call.\n\n## API\n\n### `createPriorIdentity(config)`\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `clientId` | `string` | required | Your OIDC client / relying-party id. Must match token `aud`. |\n| `issuer` | `string` | `https://api.cg3.io` | Expected issuer claim and base for discovery defaults. |\n| `discoveryUrl` | `string` | `{issuer}/.well-known/openid-configuration` | Optional override for OIDC discovery. |\n| `jwksUrl` | `string` | `{issuer}/.well-known/jwks.json` | Optional override for JWKS verification. |\n| `userinfoUrl` | `string` | discovered or `{issuer}/userinfo` | Optional override for OIDC UserInfo. |\n| `tokenEnvVar` | `string` | `PRIOR_IDENTITY_ACCESS_TOKEN` | Env var for stdio delegated identity token validation. |\n| `onNewUser` | `(user, token) => Promise<void>` | -- | Called on first visit from a new delegated subject. |\n| `resolveUser` | `(subject) => Promise<unknown>` | -- | Return truthy to skip `onNewUser` for known users. |\n\n### `identity.validate(token): Promise<PriorUser | null>`\n\nValidates a delegated bearer token locally with cached JWKS.\n\nRequired token contract:\n\n- `type=\"access\"`\n- `scope` containing `identity:read`\n\nReturns `null` for invalid, expired, wrong-audience, wrong-issuer, or unsupported tokens.\n\n### `identity.validateEnv(): Promise<PriorUser | null>`\n\nReads the token from `PRIOR_IDENTITY_ACCESS_TOKEN` (or your custom env var) and validates it once.\n\n### `identity.getUserInfo(token): Promise<PriorUserInfo | null>`\n\nCalls Prior's standard OIDC UserInfo endpoint. The SDK discovers `userinfo_endpoint` from OIDC metadata unless you override `userinfoUrl`.\n\n`validate()` does not include email or other richer profile claims. Use `getUserInfo()` when you need that data.\n\nTypical response shape when those claims are available for the delegated session:\n\n```typescript\n{\n  sub: \"tool-scoped-subject\",\n  name: \"Alice\",\n  email: \"alice@example.com\",\n  email_verified: true\n}\n```\n\n### `identity.getEmail(token): Promise<string | null>`\n\nConvenience helper on top of `getUserInfo()`. Useful for account linking during first-visit provisioning when you only need the email claim.\n\n### `identity.connectInteractive(options?): Promise<PriorUser | null>`\n\nNode-only browser flow for manual users. Resolution order:\n\n1. Check `~/.prior/identity/{clientId}.json` for a persisted delegated token.\n2. If missing or expired, discover OIDC endpoints from the issuer.\n3. Open browser for `/authorize` with PKCE.\n4. Exchange the returned code at `/token`.\n5. Persist the delegated access token for next startup.\n\nOptions:\n\n```typescript\n{\n  timeout?: number;\n  authorizeUrl?: string;\n  tokenUrl?: string;\n  headless?: boolean;\n  onUrl?: (url: string) => void;\n}\n```\n\n### `PriorUser`\n\n```typescript\ninterface PriorUser {\n  subject: string;     // Preferred field for the pairwise delegated subject.\n  displayName: string; // Human-readable and mutable.\n  audience: string;    // The relying party / client id from `aud`.\n  jti: string;         // Unique token id.\n}\n```\n\n## Security model\n\nEvery `validate()` call checks:\n\n- ES256 signature against Prior's JWKS\n- `iss`\n- `aud`\n- `exp`\n- delegated access-token family (`type=\"access\"` with `identity:read`)\n- required `sub` and `jti`\n\nAll of that is local.\n\n### Revocation caveat\n\nThis package does not call a revocation endpoint on every request. A revoked token can remain usable until it expires. That is the tradeoff for local-only validation.\n\n### Connect flow\n\n`connectInteractive()` uses a standard auth-code + PKCE loopback flow:\n\n- cryptographic `state` for CSRF protection\n- `code_verifier` / `code_challenge` for PKCE\n- `127.0.0.1` callback on an OS-assigned port\n- token validation through the same `validate()` path before acceptance\n\n## Account linking\n\nFor servers with existing users, use `onNewUser` plus `getEmail()` or `getUserInfo()` to link delegated subjects to your own user records:\n\n```typescript\nconst identity = createPriorIdentity({\n  clientId: \"my-tool\",\n  onNewUser: async (priorUser, token) => {\n    const email = await identity.getEmail(token);\n    const existing = email ? await db.users.findByEmail(email) : null;\n    if (existing) {\n      await db.users.update(existing.id, { priorSubject: priorUser.subject });\n    } else {\n      await db.users.create({\n        priorSubject: priorUser.subject,\n        displayName: priorUser.displayName,\n      });\n    }\n  },\n  resolveUser: async (subject) => db.users.findByPriorSubject(subject),\n});\n```\n\n## Current contract\n\n- Supported interactive delegated flow: `/authorize` + `/token` + `/userinfo`\n- Supported local validation contract: ES256 delegated `type=\"access\"` token with `scope` containing `identity:read`\n- This SDK uses `clientId`, `user.subject`, `authorizeUrl`, `tokenUrl`, and `PRIOR_IDENTITY_ACCESS_TOKEN`\n\n## Reliability and exit cost\n\nIf Prior is unavailable:\n\n- existing users with valid cached tokens keep working until token expiry\n- new token issuance and manual connect pause\n- `getUserInfo()` / `getEmail()` return `null`\n- your existing fallback auth can keep working if you have one\n\nExit cost stays low because the integration is standard JWT validation plus OIDC metadata:\n\n- `iss`\n- `aud`\n- `sub`\n- `exp`\n- `jti`\n- `type: \"access\"` with `scope` containing `identity:read`\n- optional profile claims such as `name`\n","readmeFilename":"README.md"}