{"_id":"@access-tokens/client","_rev":"3-a81778242cb311e637d689722125c86a","name":"@access-tokens/client","dist-tags":{"latest":"1.1.0"},"versions":{"1.0.0":{"name":"@access-tokens/client","version":"1.0.0","keywords":["dynamodb","personal-access-token","pat","authentication","client","api-client"],"author":{"name":"Trevor Robinson"},"license":"ISC","_id":"@access-tokens/client@1.0.0","maintainers":[{"name":"trevorr","email":"trevor@scurrilous.com"}],"homepage":"https://github.com/loancrate/access-tokens#readme","bugs":{"url":"https://github.com/loancrate/access-tokens/issues"},"dist":{"shasum":"59f6a6c892ff2840b0a256159666e5e60de82528","tarball":"https://registry.npmjs.org/@access-tokens/client/-/client-1.0.0.tgz","fileCount":23,"integrity":"sha512-tykS1Oi0Xh0/HcZEuBOYzSC8emZaOU+p5WSDgt6dOS8I5OxAdrSPcqrQdM2T/eUUkanF+cB0Ti0ZehD15mO9MA==","signatures":[{"sig":"MEQCIFWufcMV0pKkuAfPzEnjbxR06c1hqCHkMNIG9aUthB/QAiBBx8YRzYgUAYFNvVS07nJQ97S8qQlfyk5lpXVAwi5bew==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@access-tokens%2fclient@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":46760},"main":"./dist/index.js","_from":"file:access-tokens-client-1.0.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"scripts":{"lint":"eslint src","test":"jest","build":"tsc --build tsconfig.build.json","clean":"rm -rf dist *.tsbuildinfo coverage *-junit.xml","typecheck":"tsc --noEmit"},"_npmUser":{"name":"trevorr","email":"trevor@scurrilous.com"},"_resolved":"/tmp/799ba3a504dc9fc58aade06a5f0e561c/access-tokens-client-1.0.0.tgz","_integrity":"sha512-tykS1Oi0Xh0/HcZEuBOYzSC8emZaOU+p5WSDgt6dOS8I5OxAdrSPcqrQdM2T/eUUkanF+cB0Ti0ZehD15mO9MA==","repository":{"url":"git+https://github.com/loancrate/access-tokens.git","type":"git","directory":"packages/client"},"_npmVersion":"11.6.1","description":"Client library for personal access token authentication service","directories":{},"_nodeVersion":"24.11.0","dependencies":{"zod":"^4.1.12","fetch-retry":"^6.0.0","catch-unknown":"^2.0.0"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","eslint":"^9.38.0","ts-jest":"^29.4.5","jest-junit":"^16.0.0","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^24.9.2","@jest/globals":"^30.2.0","@access-tokens/express":"1.0.0","@access-tokens/jest-config":"1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/client_1.0.0_1762480580383_0.5343154731922788","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@access-tokens/client","version":"1.1.0","description":"Client library for personal access token authentication service","keywords":["dynamodb","personal-access-token","pat","authentication","client","api-client"],"repository":{"type":"git","url":"git+https://github.com/loancrate/access-tokens.git","directory":"packages/client"},"license":"ISC","author":{"name":"Trevor Robinson"},"main":"./dist/index.js","types":"./dist/index.d.ts","dependencies":{"catch-unknown":"^2.0.0","fetch-retry":"^6.0.0","zod":"^4.1.12"},"devDependencies":{"@jest/globals":"^30.2.0","@types/jest":"^30.0.0","@types/node":"^24.9.2","eslint":"^9.38.0","jest":"^30.2.0","jest-junit":"^16.0.0","ts-jest":"^29.4.5","typescript":"^5.9.3","@access-tokens/express":"1.1.0","@access-tokens/jest-config":"1.0.1"},"engines":{"node":">=20.0.0"},"scripts":{"build":"tsc --build tsconfig.build.json","clean":"rm -rf dist *.tsbuildinfo coverage *-junit.xml","lint":"eslint src","test":"jest","typecheck":"tsc --noEmit"},"_id":"@access-tokens/client@1.1.0","bugs":{"url":"https://github.com/loancrate/access-tokens/issues"},"homepage":"https://github.com/loancrate/access-tokens#readme","_integrity":"sha512-zW3jgljq1n8SuSkmLMNgDd5tLYQ9tRvtKa5RnB+/TcjaxjyiECqFx6WbPFEhCyrW5Y+0iInVzbScVxUigxGm/Q==","_resolved":"/tmp/f302f590d7a0cb9480698fb0d878badd/access-tokens-client-1.1.0.tgz","_from":"file:access-tokens-client-1.1.0.tgz","_nodeVersion":"24.12.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-zW3jgljq1n8SuSkmLMNgDd5tLYQ9tRvtKa5RnB+/TcjaxjyiECqFx6WbPFEhCyrW5Y+0iInVzbScVxUigxGm/Q==","shasum":"934b2dc31a7894074bdad1eb6c3b237642b8a5f1","tarball":"https://registry.npmjs.org/@access-tokens/client/-/client-1.1.0.tgz","fileCount":23,"unpackedSize":48902,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@access-tokens%2fclient@1.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIA6KPiIF7XDTiYi/a91+aRjthLnY0X08JiEMSVREReU/AiBzPMgum+/f1uJaQrD6nKiw38+HqrCQyL6auh1CKFW1tw=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:496df2cc-d458-4fcd-a39f-125731eb974a"}},"directories":{},"maintainers":[{"name":"trevorr","email":"trevor@scurrilous.com"},{"name":"andrew-loancrate","email":"andrew@loancrate.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/client_1.1.0_1767127271041_0.3906907735831118"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-07T01:56:20.277Z","modified":"2025-12-30T20:41:11.557Z","1.0.0":"2025-11-07T01:56:20.628Z","1.1.0":"2025-12-30T20:41:11.194Z"},"bugs":{"url":"https://github.com/loancrate/access-tokens/issues"},"author":{"name":"Trevor Robinson"},"license":"ISC","homepage":"https://github.com/loancrate/access-tokens#readme","keywords":["dynamodb","personal-access-token","pat","authentication","client","api-client"],"repository":{"type":"git","url":"git+https://github.com/loancrate/access-tokens.git","directory":"packages/client"},"description":"Client library for personal access token authentication service","maintainers":[{"name":"trevorr","email":"trevor@scurrilous.com"},{"name":"andrew-loancrate","email":"andrew@loancrate.com"}],"readme":"# @access-tokens/client\n\n[![npm](https://img.shields.io/npm/v/@access-tokens/client)](https://www.npmjs.com/package/@access-tokens/client)\n[![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)\n\nType-safe HTTP client for Personal Access Token (PAT) authentication services built with @access-tokens/express.\n\n## Features\n\n- **Type-Safe API**: Full TypeScript support with Zod schema validation\n- **Automatic JWT Management**: Handles PAT-to-JWT exchange and renewal\n- **Retry Logic**: Built-in exponential backoff with fetch-retry\n- **Error Handling**: Comprehensive error types with detailed messages\n- **Admin Operations**: Full token lifecycle management\n- **Zero Dependencies**: Uses native fetch (Node.js 18+)\n\n## Installation\n\n```bash\nnpm install @access-tokens/client\n```\n\n## Quick Start\n\n```typescript\nimport { AccessTokensClient } from \"@access-tokens/client\";\n\n// Initialize client with PAT\nconst client = new AccessTokensClient({\n  endpoint: \"https://api.example.com\",\n  apiKey: \"pat_abc123...\", // Your Personal Access Token\n});\n\n// List all tokens (requires admin PAT)\nconst tokens = await client.list();\nconsole.log(\"Total tokens:\", tokens.length);\n\n// Issue a new token\nconst { token, record } = await client.issue({\n  owner: \"user@example.com\",\n  isAdmin: false,\n  expiresAt: Math.floor(Date.now() / 1000) + 365 * 24 * 60 * 60, // 1 year\n});\n\nconsole.log(\"New token:\", token);\nconsole.log(\"Token ID:\", record.tokenId);\n```\n\n## API Reference\n\n### Constructor\n\n#### `new AccessTokensClient(options)`\n\nCreates a new client instance.\n\n**Options:**\n\n```typescript\n{\n  endpoint: string;        // Base URL of your PAT API (e.g., \"https://api.example.com\")\n  apiKey: string;          // Your Personal Access Token\n  authPath?: string;       // Auth endpoint path (default: \"/auth\")\n  adminPath?: string;      // Admin endpoint path (default: \"/admin\")\n  fetch?: typeof fetch;    // Custom fetch implementation (optional)\n}\n```\n\n**Example:**\n\n```typescript\nconst client = new AccessTokensClient({\n  endpoint: \"https://api.example.com\",\n  apiKey: process.env.PAT_TOKEN!,\n  authPath: \"/auth\", // optional, default is \"/auth\"\n  adminPath: \"/admin\", // optional, default is \"/admin\"\n});\n```\n\n### Token Operations\n\nAll methods require an admin PAT unless otherwise noted.\n\n#### `list(options?): Promise<PatRecord[]>`\n\nLists all tokens.\n\n**Options:**\n\n```typescript\n{\n  includeRevoked?: boolean;     // Include revoked tokens (default: false)\n  includeExpired?: boolean;     // Include expired tokens (default: false)\n  includeSecretPhc?: boolean;   // Include secret hashes (default: false)\n  hasRole?: string;             // Filter tokens that have this role (optional)\n  limit?: number;               // Max results per page\n  afterTokenId?: string;        // Pagination token (tokenId to start after)\n}\n```\n\n**Example:**\n\n```typescript\n// List all active tokens\nconst tokens = await client.list();\n\n// List all tokens including revoked and expired\nconst allTokens = await client.list({\n  includeRevoked: true,\n  includeExpired: true,\n});\n\n// Paginated listing\nconst page1 = await client.list({ limit: 10 });\nconst page2 = await client.list({\n  limit: 10,\n  afterTokenId: page1[page1.length - 1].tokenId, // Use last token ID from previous page\n});\n```\n\n#### `issue(params): Promise<{ token: string; record: PatRecord }>`\n\nIssues a new token.\n\n**Parameters:**\n\n```typescript\n{\n  owner: string;           // Token owner (e.g., email address)\n  isAdmin?: boolean;       // Whether token has admin privileges (default: false)\n  roles?: string[];        // Array of role strings (optional, max 50 roles, 100 chars each)\n  expiresAt?: number;      // Unix timestamp for expiration (optional)\n}\n```\n\n**Example:**\n\n```typescript\nconst { token, record } = await client.issue({\n  owner: \"user@example.com\",\n  isAdmin: false,\n  expiresAt: Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60, // 30 days\n});\n\n// Give token to user securely\nconsole.log(\"Token (show once):\", token);\nconsole.log(\"Token ID:\", record.tokenId);\n```\n\n#### `register(params): Promise<TokenRecord>`\n\nRegisters a pre-generated token.\n\n**Parameters:**\n\n```typescript\n{\n  tokenId: string;         // Pre-generated token ID\n  secretPhc: string;       // PHC-formatted secret hash\n  owner: string;           // Token owner\n  isAdmin?: boolean;       // Admin status (default: false)\n  roles?: string[];        // Array of role strings (optional)\n  expiresAt?: number;      // Expiration timestamp (optional)\n}\n```\n\n**Example:**\n\n```typescript\nawait client.register({\n  tokenId: \"pregenerated123\",\n  secretPhc: \"$scrypt$...\",\n  owner: \"user@example.com\",\n  isAdmin: false,\n});\n```\n\n#### `update(tokenId: string, updates): Promise<void>`\n\nUpdates an existing token.\n\n**Updates:**\n\n```typescript\n{\n  owner?: string;          // New owner\n  isAdmin?: boolean;       // New admin status\n  roles?: RolesUpdate;     // Update roles (see below)\n  secretPhc?: string;      // New secret hash\n  expiresAt?: number | null; // New expiration or null to remove\n}\n```\n\n**Roles Update Syntax:**\n\n```typescript\n// Replace all roles\nawait client.update(tokenId, { roles: [\"reader\", \"writer\"] });\n\n// Add roles atomically (idempotent, cannot combine with remove)\nawait client.update(tokenId, { roles: { add: [\"admin\"] } });\n\n// Remove roles atomically (idempotent, cannot combine with add)\nawait client.update(tokenId, { roles: { remove: [\"guest\"] } });\n\n// Clear all roles\nawait client.update(tokenId, { roles: [] });\n```\n\n**Example:**\n\n```typescript\n// Promote user to admin\nawait client.update(\"34NwRzvnBbgI3uedkrQ3Q\", {\n  isAdmin: true,\n});\n\n// Change owner\nawait client.update(\"34NwRzvnBbgI3uedkrQ3Q\", {\n  owner: \"newuser@example.com\",\n});\n\n// Remove expiration\nawait client.update(\"34NwRzvnBbgI3uedkrQ3Q\", {\n  expiresAt: null,\n});\n```\n\n#### `revoke(tokenId: string, options?: { expiresAt?: number }): Promise<void>`\n\nRevokes a token. Optionally sets an expiration for automatic cleanup.\n\n**Example:**\n\n```typescript\n// Revoke immediately\nawait client.revoke(\"34NwRzvnBbgI3uedkrQ3Q\");\n\n// Revoke with cleanup in 30 days\nawait client.revoke(\"34NwRzvnBbgI3uedkrQ3Q\", {\n  expiresAt: Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60,\n});\n```\n\n#### `restore(tokenId: string): Promise<void>`\n\nRestores a previously revoked token.\n\n**Example:**\n\n```typescript\nawait client.restore(\"34NwRzvnBbgI3uedkrQ3Q\");\n```\n\n## JWT Token Exchange\n\nThe client automatically handles JWT token exchange:\n\n1. On first API call, the client exchanges your PAT for a JWT\n2. JWT is used for subsequent requests\n3. When JWT expires (default: 1 hour), client automatically requests a new one\n4. Your PAT is only sent during token exchange\n\nThis design provides:\n\n- **Performance**: No DynamoDB lookup and scrypt operation on every request\n- **Security**: Short-lived JWTs reduce risk if compromised\n- **Transparency**: Automatic, no manual JWT management required\n\n## Error Handling\n\nThe client throws standard `Error` objects. The `error.cause` property may contain additional details:\n\n```typescript\nimport { AccessTokensClient, isApiError } from \"@access-tokens/client\";\n\ntry {\n  await client.revoke(\"invalid-token-id\");\n} catch (error) {\n  if (error instanceof Error) {\n    console.error(\"Message:\", error.message); // \"Failed to revoke token\"\n\n    // error.cause may be an ApiError object or a string\n    if (isApiError(error.cause)) {\n      console.error(\"API Error:\", error.cause.error.message);\n      console.error(\"Code:\", error.cause.error.code);\n      console.error(\"Details:\", error.cause.error.details);\n    } else if (typeof error.cause === \"string\") {\n      console.error(\"Status text:\", error.cause);\n    }\n  }\n}\n```\n\n**Common Error Status Codes:**\n\n- `400` - Bad request (invalid parameters)\n- `401` - Unauthorized (invalid or missing PAT/JWT)\n- `403` - Forbidden (insufficient permissions, not admin)\n- `404` - Not found (token doesn't exist)\n- `500` - Internal server error\n\n## Retry Behavior\n\nThe client uses exponential backoff for retries:\n\n- **Retryable errors**: 408, 429, 500, 502, 503, 504 status codes\n- **Non-retryable**: All other status codes\n- **Fixed policy**: 3 retries with exponential backoff starting at 1s, capped at 30s\n- **429 Rate Limits**: Respects `Retry-After` header when present\n\nThe retry policy is built-in and cannot be customized. If you need custom retry behavior, provide your own `fetch` implementation in the constructor options.\n\n## Types\n\n### `PatRecord`\n\n```typescript\ninterface PatRecord {\n  tokenId: string; // Unique token identifier (21 chars, alphanumeric)\n  owner: string; // Token owner\n  isAdmin: boolean; // Admin privileges\n  roles?: string[]; // Array of role strings\n  secretPhc?: string; // PHC hash (only if includeSecretPhc=true)\n  createdAt: number; // Unix timestamp\n  lastUsedAt?: number | null; // Unix timestamp of last use\n  expiresAt?: number | null; // Unix timestamp for expiration\n  revokedAt?: number | null; // Unix timestamp when revoked (null if not revoked)\n}\n```\n\n### `RolesUpdate`\n\n```typescript\ntype RolesUpdate =\n  | string[] // Replace all roles\n  | { add: string[] } // Atomic add (cannot combine with remove)\n  | { remove: string[] }; // Atomic remove (cannot combine with add)\n```\n\n### `ApiError`\n\nWhen API errors occur, they are provided as the `cause` property of the thrown `Error`:\n\n```typescript\ntype ApiError = {\n  error: {\n    message: string; // Error message\n    code?: string; // Optional error code\n    details?: string | Record<string, unknown>; // Additional error details\n  };\n};\n```\n\n**Usage:**\n\n```typescript\nimport { isApiError } from \"@access-tokens/client\";\n\ntry {\n  await client.list();\n} catch (error) {\n  if (error instanceof Error && isApiError(error.cause)) {\n    console.error(error.cause.error.message);\n  }\n}\n```\n\n## Requirements\n\n- Node.js 20+ (native fetch support)\n- @access-tokens/express server\n\n## Related Packages\n\n- [@access-tokens/core](https://www.npmjs.com/package/@access-tokens/core) - Core token management library\n- [@access-tokens/express](https://www.npmjs.com/package/@access-tokens/express) - Express routes and middleware\n- [@access-tokens/cli](https://www.npmjs.com/package/@access-tokens/cli) - Command-line token management\n\n## License\n\n[ISC](https://opensource.org/licenses/ISC) © 2025 Loan Crate, Inc.\n\n## Links\n\n- [GitHub Repository](https://github.com/loancrate/access-tokens)\n- [npm Package](https://www.npmjs.com/package/@access-tokens/client)\n- [Documentation](https://github.com/loancrate/access-tokens#readme)\n","readmeFilename":"README.md"}