{"_id":"@blureffect/oauth2-token-manager","_rev":"4-ffe15f7080550f0ae15bd06d6422880c","name":"@blureffect/oauth2-token-manager","dist-tags":{"latest":"0.2.1"},"versions":{"0.1.0":{"name":"@blureffect/oauth2-token-manager","version":"0.1.0","keywords":["oauth2","authentication","token-management","typescript"],"author":{"name":"Blureffect","email":"sebastian@blureffect.co"},"license":"MIT","_id":"@blureffect/oauth2-token-manager@0.1.0","maintainers":[{"name":"eabl0306","email":"ezequielbahoquelopez@gmail.com"},{"name":"sbarcenas255","email":"sebastian@blureffect.co"}],"homepage":"https://github.com/blureffect/oauth2-token-manager#readme","bugs":{"url":"https://github.com/blureffect/oauth2-token-manager/issues"},"dist":{"shasum":"0ad7a8e6ab1b7d33a773e68871e5cf44451e2011","tarball":"https://registry.npmjs.org/@blureffect/oauth2-token-manager/-/oauth2-token-manager-0.1.0.tgz","fileCount":8,"integrity":"sha512-jQFiS/6C2j32AJn4ICWJviZbPIO9tp1qxfYrBLgpIAGUtX79PoknpEvM6tpt9XXXgrsilAaFsM7/7GDlGApTmA==","signatures":[{"sig":"MEUCIAH2QwHb2RaeMtpdqigwssmESkTi/VwAB17SV4fgw/uuAiEAkARkKuwGTwlLVoZI8M9Ek3sKqBpxF2LW4yQSylDWa2o=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":529187},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"595fb26bc2cf17a1632535a7e43c2c7cc143de55","scripts":{"ci":"npm run lint && npm run typecheck && npm run test:coverage && npm run build","dev":"tsx watch src/index.ts","docs":"typedoc","lint":"eslint .","test":"vitest","build":"tsup","format":"prettier --write","prepack":"pinst --disable","prepare":"husky install","release":"npm run ci:all && changeset publish","test:ui":"vitest --ui","lint:fix":"eslint --fix .","postpack":"pinst --enable","typecheck":"tsc --noEmit","docs:watch":"typedoc --watch","test:watch":"vitest watch","build:watch":"tsup --watch","dev:example":"tsx src/examples/basic.ts","lint:config":"eslint --print-config src/index.ts","postinstall":"husky","format:check":"prettier","test:coverage":"vitest run --coverage","prepublishOnly":"npm run ci"},"_npmUser":{"name":"sbarcenas255","email":"sebastian@blureffect.co"},"repository":{"url":"git+https://github.com/blureffect/oauth2-token-manager.git","type":"git"},"_npmVersion":"11.3.0","description":"A scalable OAuth2 token management library with multi-system support","directories":{},"_nodeVersion":"22.15.0","dependencies":{"la":"link:../../../Library/pnpm/global/5/node_modules/la","ls":"link:../../../Library/pnpm/global/5/node_modules/ls","tsx":"^4.19.4","tsup":"^8.5.0","pinst":"^3.0.0","crypto":"^1.0.1","typescript":"^5.8.3","@types/node":"^22.15.29","iron-session":"^8.0.4","@changesets/cli":"^2.29.4","oauth2-token-manager":"link:../../../Library/pnpm/global/5/node_modules/@blureffect/oauth2-token-manager"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"husky":"^9.1.7","eslint":"^9.28.0","vitest":"^3.2.1","globals":"^16.2.0","typedoc":"^0.28.5","prettier":"^3.5.3","@vitest/ui":"^3.2.1","lint-staged":"^16.1.0","@commitlint/cli":"^19.8.1","@vitest/coverage-v8":"^3.2.1","eslint-config-prettier":"^10.1.5","eslint-plugin-prettier":"^5.4.1","typedoc-plugin-markdown":"^4.6.4","@typescript-eslint/parser":"^8.33.1","@commitlint/config-conventional":"^19.8.1","@typescript-eslint/eslint-plugin":"^8.33.1"},"_npmOperationalInternal":{"tmp":"tmp/oauth2-token-manager_0.1.0_1749503334245_0.6294705682980934","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@blureffect/oauth2-token-manager","version":"0.1.1","keywords":["oauth2","authentication","token-management","typescript"],"author":{"name":"Blureffect","email":"sebastian@blureffect.co"},"license":"MIT","_id":"@blureffect/oauth2-token-manager@0.1.1","maintainers":[{"name":"eabl0306","email":"ezequielbahoquelopez@gmail.com"},{"name":"sbarcenas255","email":"sebastian@blureffect.co"}],"homepage":"https://github.com/blureffect/oauth2-token-manager#readme","bugs":{"url":"https://github.com/blureffect/oauth2-token-manager/issues"},"dist":{"shasum":"f8fa855641de593ba5ebecc7e61840fc748036c8","tarball":"https://registry.npmjs.org/@blureffect/oauth2-token-manager/-/oauth2-token-manager-0.1.1.tgz","fileCount":8,"integrity":"sha512-e9QtqCIQUNFDmXyMF44n1vo/OImhsd1Co4I8KTzLPcyhPUirj0yD9Y+KUOX7clvU0zihsb/buuSM7Hx6eNXgcw==","signatures":[{"sig":"MEYCIQD4CoHGwg5X5nRo056Uk1Jj93oQScYu7b881Ko1f3z6GgIhAPmEmcl4MjlFNkU5w0834JKUmv32YaLY1pS0EtVQzO11","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":532317},"main":"./dist/index.cjs","type":"module","_from":"file:blureffect-oauth2-token-manager-0.1.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"ci":"npm run lint && npm run typecheck && npm run test:coverage && npm run build","dev":"tsx watch src/index.ts","docs":"typedoc","lint":"eslint .","test":"vitest","build":"tsup","format":"prettier --write","release":"npm run ci:all && changeset publish","test:ui":"vitest --ui","lint:fix":"eslint --fix .","typecheck":"tsc --noEmit","docs:watch":"typedoc --watch","test:watch":"vitest watch","build:watch":"tsup --watch","dev:example":"tsx src/examples/basic.ts","lint:config":"eslint --print-config src/index.ts","_postinstall":"husky","format:check":"prettier","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"sbarcenas255","email":"sebastian@blureffect.co"},"_resolved":"/private/var/folders/tl/jh9nnbpj16v44zsm3ynzms4m0000gn/T/510e44f896a39654081ce7367b37ddfc/blureffect-oauth2-token-manager-0.1.1.tgz","_integrity":"sha512-e9QtqCIQUNFDmXyMF44n1vo/OImhsd1Co4I8KTzLPcyhPUirj0yD9Y+KUOX7clvU0zihsb/buuSM7Hx6eNXgcw==","repository":{"url":"git+https://github.com/blureffect/oauth2-token-manager.git","type":"git"},"_npmVersion":"11.3.0","description":"A scalable OAuth2 token management library with multi-system support","directories":{},"_nodeVersion":"22.15.0","dependencies":{"la":"link:../../../Library/pnpm/global/5/node_modules/la","ls":"link:../../../Library/pnpm/global/5/node_modules/ls","tsx":"^4.19.4","tsup":"^8.5.0","pinst":"^3.0.0","crypto":"^1.0.1","typescript":"^5.8.3","@types/node":"^22.15.29","iron-session":"^8.0.4","@changesets/cli":"^2.29.4","oauth2-token-manager":"link:../../../Library/pnpm/global/5/node_modules/@blureffect/oauth2-token-manager"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"husky":"^9.1.7","eslint":"^9.28.0","vitest":"^3.2.1","globals":"^16.2.0","typedoc":"^0.28.5","prettier":"^3.5.3","@vitest/ui":"^3.2.1","lint-staged":"^16.1.0","@commitlint/cli":"^19.8.1","@vitest/coverage-v8":"^3.2.1","eslint-config-prettier":"^10.1.5","eslint-plugin-prettier":"^5.4.1","typedoc-plugin-markdown":"^4.6.4","@typescript-eslint/parser":"^8.33.1","@commitlint/config-conventional":"^19.8.1","@typescript-eslint/eslint-plugin":"^8.33.1"},"_npmOperationalInternal":{"tmp":"tmp/oauth2-token-manager_0.1.1_1755789440581_0.7603343341983764","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@blureffect/oauth2-token-manager","version":"0.2.0","keywords":["oauth2","authentication","token-management","typescript"],"author":{"name":"Blureffect","email":"sebastian@blureffect.co"},"license":"MIT","_id":"@blureffect/oauth2-token-manager@0.2.0","maintainers":[{"name":"eabl0306","email":"ezequielbahoquelopez@gmail.com"},{"name":"sbarcenas255","email":"sebastian@blureffect.co"}],"homepage":"https://github.com/blureffect/oauth2-token-manager#readme","bugs":{"url":"https://github.com/blureffect/oauth2-token-manager/issues"},"dist":{"shasum":"e2425abc0848ca2982382ae4a0e3b2323df9b7b6","tarball":"https://registry.npmjs.org/@blureffect/oauth2-token-manager/-/oauth2-token-manager-0.2.0.tgz","fileCount":8,"integrity":"sha512-+0iHON4LIqUeu87nHurvTd4Z6F14i0/zuqpPx+/YGK88YJiR3ys/4w+XNX35KBogzoL3QZyy1kwwo3hbA+oaYw==","signatures":[{"sig":"MEYCIQCd/uj13WZe2pvCg+7Tt2OCEr+/+pOKiQ9GKIgScIv3wgIhAL5ZRmYhtWmo4TXX2mcaFAQcl65ZtihQBw67Q9LziIh6","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":563012},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"d327feebf5a7dd3f70ef6616112b7ea5979ff5f9","scripts":{"ci":"npm run lint && npm run typecheck && npm run test:coverage && npm run build","dev":"tsx watch src/index.ts","docs":"typedoc","lint":"eslint .","test":"vitest","build":"tsup","format":"prettier --write","prepack":"pinst --disable","prepare":"husky install","release":"npm run ci:all && changeset publish","test:ui":"vitest --ui","lint:fix":"eslint --fix .","postpack":"pinst --enable","typecheck":"tsc --noEmit","docs:watch":"typedoc --watch","test:watch":"vitest watch","build:watch":"tsup --watch","dev:example":"tsx src/examples/basic.ts","lint:config":"eslint --print-config src/index.ts","postinstall":"husky","format:check":"prettier","test:coverage":"vitest run --coverage","prepublishOnly":"npm run ci"},"_npmUser":{"name":"sbarcenas255","email":"sebastian@blureffect.co"},"repository":{"url":"git+https://github.com/blureffect/oauth2-token-manager.git","type":"git"},"_npmVersion":"11.3.0","description":"A scalable OAuth2 token management library with multi-system support","directories":{},"_nodeVersion":"22.15.0","dependencies":{"la":"link:../../../Library/pnpm/global/5/node_modules/la","ls":"link:../../../Library/pnpm/global/5/node_modules/ls","tsx":"^4.19.4","tsup":"^8.5.0","pinst":"^3.0.0","crypto":"^1.0.1","typescript":"^5.8.3","@types/node":"^22.15.29","iron-session":"^8.0.4","@changesets/cli":"^2.29.4","oauth2-token-manager":"link:../../../Library/pnpm/global/5/node_modules/@blureffect/oauth2-token-manager"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"husky":"^9.1.7","eslint":"^9.28.0","vitest":"^3.2.1","globals":"^16.2.0","typedoc":"^0.28.5","prettier":"^3.5.3","@vitest/ui":"^3.2.1","lint-staged":"^16.1.0","@commitlint/cli":"^19.8.1","@vitest/coverage-v8":"^3.2.1","eslint-config-prettier":"^10.1.5","eslint-plugin-prettier":"^5.4.1","typedoc-plugin-markdown":"^4.6.4","@typescript-eslint/parser":"^8.33.1","@commitlint/config-conventional":"^19.8.1","@typescript-eslint/eslint-plugin":"^8.33.1"},"_npmOperationalInternal":{"tmp":"tmp/oauth2-token-manager_0.2.0_1755794734903_0.801220054920281","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@blureffect/oauth2-token-manager","version":"0.2.1","description":"A scalable OAuth2 token management library with multi-system support","keywords":["oauth2","authentication","token-management","typescript"],"homepage":"https://github.com/blureffect/oauth2-token-manager#readme","bugs":{"url":"https://github.com/blureffect/oauth2-token-manager/issues"},"repository":{"type":"git","url":"git+https://github.com/blureffect/oauth2-token-manager.git"},"license":"MIT","author":{"name":"Blureffect","email":"sebastian@blureffect.co"},"type":"module","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","scripts":{"dev":"tsx watch src/index.ts","dev:example":"tsx src/examples/basic.ts","build":"tsup","build:watch":"tsup --watch","test":"vitest","test:ui":"vitest --ui","test:coverage":"vitest run --coverage","test:watch":"vitest watch","lint":"eslint .","lint:fix":"eslint --fix .","lint:config":"eslint --print-config src/index.ts","format":"prettier --write","format:check":"prettier","typecheck":"tsc --noEmit","docs":"typedoc","docs:watch":"typedoc --watch","ci":"npm run lint && npm run typecheck && npm run test:coverage && npm run build","prepublishOnly":"npm run ci","prepare":"husky install","release":"npm run ci:all && changeset publish","postinstall":"husky","prepack":"pinst --disable","postpack":"pinst --enable"},"dependencies":{"@changesets/cli":"^2.29.4","@types/node":"^22.15.29","crypto":"^1.0.1","iron-session":"^8.0.4","la":"link:../../../Library/pnpm/global/5/node_modules/la","ls":"link:../../../Library/pnpm/global/5/node_modules/ls","oauth2-token-manager":"link:../../../Library/pnpm/global/5/node_modules/@blureffect/oauth2-token-manager","pinst":"^3.0.0","tsup":"^8.5.0","tsx":"^4.19.4","typescript":"^5.8.3"},"devDependencies":{"@commitlint/cli":"^19.8.1","@commitlint/config-conventional":"^19.8.1","@typescript-eslint/eslint-plugin":"^8.33.1","@typescript-eslint/parser":"^8.33.1","@vitest/coverage-v8":"^3.2.1","@vitest/ui":"^3.2.1","eslint":"^9.28.0","eslint-config-prettier":"^10.1.5","eslint-plugin-prettier":"^5.4.1","globals":"^16.2.0","husky":"^9.1.7","lint-staged":"^16.1.0","prettier":"^3.5.3","typedoc":"^0.28.5","typedoc-plugin-markdown":"^4.6.4","vitest":"^3.2.1"},"engines":{"node":">=16.0.0"},"publishConfig":{"access":"public"},"_id":"@blureffect/oauth2-token-manager@0.2.1","gitHead":"f4284890392ddeb736c65158f9d003d87f5de509","_nodeVersion":"22.15.0","_npmVersion":"11.3.0","dist":{"integrity":"sha512-H/UEcTKRUtS/1rBVWP8bdOZSLXeVTE6ha8+K7yxTwfvwfH/+GwGPU9mI8XWuvOnlv7WzzJvWrQFwaLiLMv/Gzw==","shasum":"3c13472b927ea96ef1b3fc0aa38e12f40c05f272","tarball":"https://registry.npmjs.org/@blureffect/oauth2-token-manager/-/oauth2-token-manager-0.2.1.tgz","fileCount":8,"unpackedSize":567984,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCKN/bBHXfbY4Mido3EuxNI5nL0woQHcOGPez/6h9GG6AIhALlJYyhNZ65EmChCHjzW7/aJ96WOP5ziU8s4RRJ+cSuX"}]},"_npmUser":{"name":"sbarcenas255","email":"sebastian@blureffect.co"},"directories":{},"maintainers":[{"name":"eabl0306","email":"ezequielbahoquelopez@gmail.com"},{"name":"sbarcenas255","email":"sebastian@blureffect.co"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/oauth2-token-manager_0.2.1_1755797325133_0.02350707826771714"},"_hasShrinkwrap":false}},"time":{"created":"2025-06-09T21:08:54.131Z","modified":"2025-08-21T17:28:45.545Z","0.1.0":"2025-06-09T21:08:54.573Z","0.1.1":"2025-08-21T15:17:20.782Z","0.2.0":"2025-08-21T16:45:35.086Z","0.2.1":"2025-08-21T17:28:45.352Z"},"bugs":{"url":"https://github.com/blureffect/oauth2-token-manager/issues"},"author":{"name":"Blureffect","email":"sebastian@blureffect.co"},"license":"MIT","homepage":"https://github.com/blureffect/oauth2-token-manager#readme","keywords":["oauth2","authentication","token-management","typescript"],"repository":{"type":"git","url":"git+https://github.com/blureffect/oauth2-token-manager.git"},"description":"A scalable OAuth2 token management library with multi-system support","maintainers":[{"name":"eabl0306","email":"ezequielbahoquelopez@gmail.com"},{"name":"sbarcenas255","email":"sebastian@blureffect.co"}],"readme":"# OAuth2 Token Manager\n\nA powerful, storage-agnostic OAuth2 token management library built for scalable multi-system architectures. This library provides comprehensive token lifecycle management with pluggable storage adapters, built-in security features, and support for multiple OAuth2 providers.\n\n## 🚀 Features\n\n- **🔌 Storage Agnostic**: Use any storage backend (In-Memory, PostgreSQL, or build your own adapter)\n- **🏢 Multi-System Support**: Manage tokens across multiple applications/systems\n- **🔐 Advanced Security**: PKCE support, state validation, token encryption\n- **⚡ High Performance**: Efficient token validation, caching, and refresh strategies\n- **🔄 Auto-Refresh**: Automatic token refresh with configurable buffers\n- **👤 User Management**: Comprehensive user lifecycle with email/external ID support\n- **📧 Profile Integration**: Automatic profile fetching from OAuth providers\n- **🎯 Flexible Scoping**: Fine-grained permission management\n- **💡 Developer Friendly**: Both context-managed and granular APIs\n- **🧪 Fully Tested**: Comprehensive test coverage with Vitest\n\n## 📦 Installation\n\n```bash\nnpm install @blureffect/oauth2-token-manager\n```\n\n### Storage Adapters\n\n```bash\n# PostgreSQL adapter\nnpm install @blureffect/oauth2-storage-postgres\n```\n\n## 🚀 Quick Start\n\n### Simple Setup\n\n```typescript\nimport { OAuth2Client } from '@blureffect/oauth2-token-manager';\n\n// Quick setup for common use cases\nconst oauth = await OAuth2Client.quickSetup('MyApp', {\n  google: {\n    clientId: 'your-google-client-id',\n    clientSecret: 'your-google-client-secret',\n    authorizationUrl: 'https://accounts.google.com/o/oauth2/auth',\n    tokenUrl: 'https://oauth2.googleapis.com/token',\n    redirectUri: 'http://localhost:3000/auth/callback',\n    scopes: ['profile', 'email'],\n  },\n  github: {\n    clientId: 'your-github-client-id',\n    clientSecret: 'your-github-client-secret',\n    authorizationUrl: 'https://github.com/login/oauth/authorize',\n    tokenUrl: 'https://github.com/login/oauth/access_token',\n    redirectUri: 'http://localhost:3000/auth/callback',\n    scopes: ['user:email'],\n  },\n});\n\n// Create or get a user\nconst user = await oauth.getOrCreateUser({\n  email: 'user@example.com',\n  metadata: { role: 'user' },\n});\n\n// Start OAuth flow\nconst { url, state } = await oauth.authorize({\n  provider: 'google',\n  scopes: ['profile', 'email'],\n});\n\n// Handle callback\nconst result = await oauth.handleCallback(code, state);\nconsole.log('User authenticated:', result.userId);\n```\n\n### Advanced Setup with Custom Storage\n\n```typescript\nimport { OAuth2Client } from '@blureffect/oauth2-token-manager';\nimport { PostgresStorageFactory } from '@blureffect/oauth2-storage-postgres';\n\n// Custom storage adapter\nconst storage = await PostgresStorageFactory.create({\n  host: 'localhost',\n  port: 5432,\n  username: 'oauth2_user',\n  password: 'secure_password',\n  database: 'oauth2_db',\n});\n\nconst oauth = new OAuth2Client({\n  storage,\n  providers: {\n    google: {\n      /* config */\n    },\n    github: {\n      /* config */\n    },\n  },\n});\n\n// Create system and scopes\nconst system = await oauth.createSystem('MyApp');\nconst scope = await oauth.createScope('api-access', {\n  type: 'access',\n  permissions: ['read:profile', 'write:data'],\n  isolated: true,\n});\n```\n\n## 🏗️ Architecture\n\n### Core Components\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│                     OAuth2Client                            │\n│  ┌─────────────────┐    ┌─────────────────────────────────┐ │\n│  │  Context API    │    │      Granular API              │ │\n│  │  (Simplified)   │    │  (Full Control)                │ │\n│  └─────────────────┘    └─────────────────────────────────┘ │\n└─────────────────────────────────────────────────────────────┘\n                              │\n              ┌───────────────┼───────────────┐\n              │               │               │\n    ┌─────────▼────────┐ ┌───▼────────┐ ┌───▼─────────┐\n    │   Providers      │ │  Storage   │ │   Profile   │\n    │   (OAuth2)       │ │  Adapter   │ │  Fetchers   │\n    └──────────────────┘ └────────────┘ └─────────────┘\n```\n\n### Data Model & Token Hierarchy\n\n**Important**: Users can have multiple tokens for the same provider within the same scope. This allows for scenarios like different email accounts or token refresh cycles.\n\n```typescript\n// Systems: Top-level applications/services\ninterface System {\n  id: string;\n  name: string;\n  description?: string;\n  scopes: Scope[];\n  metadata?: Record<string, any>;\n}\n\n// Scopes: Permission boundaries within systems\ninterface Scope {\n  id: string;\n  systemId: string;\n  name: string;\n  type: 'authentication' | 'access' | 'custom';\n  permissions: string[];\n  isolated: boolean; // Whether tokens are isolated to this scope\n}\n\n// Users: Identity within a system\ninterface User {\n  id: string;\n  systemId: string;\n  metadata?: Record<string, any>;\n}\n\n// User Tokens: OAuth2 tokens tied to user/system/scope/provider\n// A user can have MULTIPLE tokens for the same provider/scope combination\ninterface UserToken {\n  id: string;\n  userId: string;\n  systemId: string;\n  scopeId: string;\n  provider: string;\n  token: OAuth2Token;\n}\n```\n\n### Token Hierarchy Rules\n\n1. **One User** belongs to **One System**\n2. **One User** can have tokens in **Multiple Scopes** within their system\n3. **One User** in **One Scope** can have tokens from **Multiple Providers**\n4. **One User** in **One Scope** from **One Provider** can have **Multiple Tokens**\n5. **Email Uniqueness**: For the same provider, a user cannot have multiple tokens with the same email (validated via profile fetcher)\n6. **Cross-Provider Emails**: The same email can exist across different providers\n\n## 📚 API Reference\n\n### OAuth2Client\n\nThe main client class providing both context-managed and granular APIs.\n\n#### Context-Managed API (Recommended)\n\n```typescript\n// System management\nawait oauth.createSystem('MyApp');\nawait oauth.useSystem(systemId);\n\n// User management\nconst user = await oauth.getOrCreateUser({ email: 'user@example.com' });\nawait oauth.useUser(userId);\n\n// Authorization flow\nconst { url, state } = await oauth.authorize({ provider: 'google' });\nconst result = await oauth.handleCallback(code, state);\n\n// Token operations (uses current context + default scope)\n// Note: When multiple tokens exist, these methods use the first (most recent) token\nconst accessToken = await oauth.getAccessToken('google');\nconst validToken = await oauth.ensureValidToken('google');\n\n// Get all user tokens with auto-refresh (for current user)\nconst userTokens = await oauth.getUserTokens();\n\n// Get all valid tokens for a specific user\nconst allTokens = await oauth.getAllValidTokensForUser(userId);\n// Returns: { provider: string; scopeId: string; token: OAuth2Token; userToken: UserToken }[]\n\n// Revoke tokens (uses current context)\nawait oauth.revokeTokens('google'); // Revokes for current user/scope/provider\n```\n\n#### Granular API (Advanced)\n\nThe granular API provides full control over the token hierarchy:\n\n```typescript\n// === User-Centric Token Queries (Primary Key: User) ===\n\n// Get ALL tokens for a user across all scopes/providers\nconst userTokens = await oauth.granular.getTokensByUser(userId);\n\n// Get tokens for user in specific scope (across all providers)\nconst scopeTokens = await oauth.granular.getTokensByUserAndScope(userId, scopeId);\n\n// Get tokens for user with specific provider (across all scopes)\nconst providerTokens = await oauth.granular.getTokensByUserAndProvider(userId, 'google');\n\n// Get tokens for user/scope/provider combination (can be multiple!)\nconst specificTokens = await oauth.granular.getTokensByUserScopeProvider(userId, scopeId, 'google');\n\n// === Cross-User Queries (System/Scope Level) ===\n\n// Get all tokens in a scope across all users\nconst scopeAllTokens = await oauth.granular.getTokensByScope(systemId, scopeId);\n\n// Get all tokens for a provider across all users in system\nconst providerAllTokens = await oauth.granular.getTokensByProvider(systemId, 'google');\n\n// Get all tokens in a system\nconst systemTokens = await oauth.granular.getTokensBySystem(systemId);\n\n// === Email-Based Queries ===\n\n// Find tokens by email (cross-user, cross-provider)\nconst emailTokens = await oauth.granular.findTokensByEmail('user@example.com', systemId);\n\n// Find tokens by email in specific scope\nconst emailScopeTokens = await oauth.granular.findTokensByEmailAndScope(\n  'user@example.com',\n  systemId,\n  scopeId,\n);\n\n// Find tokens by email for specific provider\nconst emailProviderTokens = await oauth.granular.findTokensByEmailAndProvider(\n  'user@example.com',\n  systemId,\n  'google',\n);\n\n// Find specific token by email/scope/provider (returns single token or null)\nconst specificToken = await oauth.granular.findTokenByEmailScopeProvider(\n  'user@example.com',\n  systemId,\n  scopeId,\n  'google',\n);\n\n// === Token Operations ===\n\n// Get valid token for user (auto-refresh, takes first if multiple exist)\nconst validToken = await oauth.granular.getValidTokenForUser(userId, scopeId, 'google');\n\n// Get access token for user (convenience method)\nconst accessToken = await oauth.granular.getAccessTokenForUser(userId, scopeId, 'google');\n\n// Save new token for user\nconst savedToken = await oauth.granular.saveTokenForUser(\n  userId,\n  systemId,\n  scopeId,\n  'google',\n  'user@example.com',\n  oauthToken,\n);\n\n// === Token Management ===\n\n// Delete tokens by different criteria\nawait oauth.granular.deleteTokensByUser(userId); // All tokens for user\nawait oauth.granular.deleteTokensByUserAndScope(userId, scopeId); // User's tokens in scope\nawait oauth.granular.deleteTokensByUserAndProvider(userId, 'google'); // User's tokens for provider\n```\n\n### Storage Adapters\n\n#### Built-in Memory Adapter\n\n```typescript\nimport { InMemoryStorageAdapter } from '@blureffect/oauth2-token-manager';\n\nconst storage = new InMemoryStorageAdapter();\nconst oauth = new OAuth2Client({ storage });\n```\n\n#### PostgreSQL Adapter\n\n```typescript\nimport { PostgresStorageFactory } from '@blureffect/oauth2-storage-postgres';\n\nconst storage = await PostgresStorageFactory.create({\n  host: process.env.DB_HOST,\n  port: parseInt(process.env.DB_PORT || '5432'),\n  username: process.env.DB_USER,\n  password: process.env.DB_PASSWORD,\n  database: process.env.DB_NAME,\n  ssl: process.env.NODE_ENV === 'production',\n});\n```\n\n#### Custom Storage Adapter\n\n```typescript\nimport { StorageAdapter } from '@blureffect/oauth2-token-manager';\n\nclass MyCustomAdapter implements StorageAdapter {\n  async createSystem(system) {\n    /* implement */\n  }\n  async getSystem(id) {\n    /* implement */\n  }\n  // ... implement all required methods\n}\n```\n\n### Provider Configuration\n\n#### Google OAuth2\n\n```typescript\n{\n  google: {\n    clientId: 'your-client-id',\n    clientSecret: 'your-client-secret',\n    authorizationUrl: 'https://accounts.google.com/o/oauth2/auth',\n    tokenUrl: 'https://oauth2.googleapis.com/token',\n    redirectUri: 'http://localhost:3000/auth/callback',\n    scopes: ['profile', 'email'],\n    profileUrl: 'https://www.googleapis.com/oauth2/v2/userinfo',\n    usePKCE: true, // Recommended for security\n  }\n}\n```\n\n#### GitHub OAuth2\n\n```typescript\n{\n  github: {\n    clientId: 'your-client-id',\n    clientSecret: 'your-client-secret',\n    authorizationUrl: 'https://github.com/login/oauth/authorize',\n    tokenUrl: 'https://github.com/login/oauth/access_token',\n    redirectUri: 'http://localhost:3000/auth/callback',\n    scopes: ['user:email'],\n    profileUrl: 'https://api.github.com/user',\n  }\n}\n```\n\n#### Generic Provider\n\n```typescript\n{\n  custom: {\n    clientId: 'your-client-id',\n    clientSecret: 'your-client-secret',\n    authorizationUrl: 'https://provider.com/oauth/authorize',\n    tokenUrl: 'https://provider.com/oauth/token',\n    redirectUri: 'http://localhost:3000/auth/callback',\n    scopes: ['read', 'write'],\n    profileUrl: 'https://provider.com/api/user',\n    additionalParams: {\n      audience: 'api.example.com'\n    },\n    responseRootKey: 'data' // For nested responses\n  }\n}\n```\n\n#### Google OAuth2 with Offline Access\n\n```typescript\nconst oauth = new OAuth2Client({\n  providers: {\n    google: {\n      clientId: 'your-client-id',\n      clientSecret: 'your-client-secret',\n      redirectUri: 'http://localhost:3000/auth/callback',\n      scopes: ['profile', 'email'],\n      // Override default offline access parameters\n      extraAuthParams: {\n        access_type: 'offline', // Request refresh token\n        prompt: 'consent', // Force consent screen\n        include_granted_scopes: 'true', // Include previously granted scopes\n      },\n    },\n  },\n});\n\n// The library automatically handles refresh tokens\nconst token = await oauth.getAccessToken('google', {\n  autoRefresh: true,\n  refreshBuffer: 5, // Refresh 5 minutes before expiry\n});\n```\n\n#### Customizing Authorization Parameters\n\nEach provider supports customization through `extraAuthParams` and `additionalParams`:\n\n```typescript\n{\n  google: {\n    // ... other config ...\n    extraAuthParams: {\n      access_type: 'offline',    // For refresh tokens\n      prompt: 'select_account',  // Force account selection\n      hd: 'yourdomain.com'      // Limit to specific Google Workspace domain\n    }\n  },\n  microsoft: {\n    // ... other config ...\n    extraAuthParams: {\n      prompt: 'select_account',\n      domain_hint: 'yourdomain.com'\n    }\n  }\n}\n```\n\nAvailable parameters for Google OAuth2:\n\n- `access_type`: 'online' (default) or 'offline' (for refresh tokens)\n- `prompt`: 'none', 'consent', 'select_account'\n- `include_granted_scopes`: 'true' or 'false'\n- `login_hint`: User's email address\n- `hd`: Google Workspace domain restriction\n\n## 🔧 Advanced Features\n\n### Token Auto-Refresh\n\n```typescript\nconst accessToken = await oauth.getAccessToken('google', {\n  autoRefresh: true,\n  refreshBuffer: 5, // Refresh 5 minutes before expiry\n  expirationBuffer: 30, // Consider expired 30 seconds early\n});\n```\n\n### Profile-Based Token Management\n\n```typescript\nconst result = await oauth.handleCallback(code, state, {\n  profileOptions: {\n    checkProfileEmail: true, // Fetch and check email conflicts\n    replaceConflictingTokens: true, // Replace existing tokens with same email\n    mergeUserData: true, // Merge profile data into user metadata\n  },\n});\n```\n\n### Email-Based Operations\n\n```typescript\n// Get all valid tokens for an email across all providers in a system\n// Note: This returns an array since one email can have tokens from multiple providers\nconst emailTokens = await oauth.getAllValidTokensForEmail('user@example.com', systemId);\n// Returns: { provider: string; scopeId: string; token: OAuth2Token; userToken: UserToken }[]\n\n// Get specific token by email (returns single token or null)\n// This enforces the email uniqueness rule within provider/scope\nconst token = await oauth.getTokenForEmail('user@example.com', systemId, scopeId, 'google');\n\n// Get valid token for email with auto-refresh\nconst validToken = await oauth.getValidTokenForEmail(\n  'user@example.com',\n  systemId,\n  scopeId,\n  'google',\n  { autoRefresh: true },\n);\n\n// Get access token for email\nconst accessToken = await oauth.getAccessTokenForEmail(\n  'user@example.com',\n  systemId,\n  scopeId,\n  'google',\n);\n\n// Execute with valid token for email\nawait oauth.withValidTokenForEmail(\n  'user@example.com',\n  systemId,\n  scopeId,\n  'google',\n  async (accessToken) => {\n    console.log('Using token for email:', accessToken);\n  },\n);\n\n// Check if email has token for specific provider/scope\nconst hasToken = await oauth.hasTokenForEmail('user@example.com', systemId, scopeId, 'google');\n\n// Revoke tokens for email\nawait oauth.revokeTokensForEmail('user@example.com', systemId, scopeId, 'google');\n```\n\n### User-Centric Operations (Stateless)\n\nFor backend APIs where you have explicit user IDs:\n\n```typescript\n// Get access token for specific user/scope/provider\n// Note: Takes the first (most recent) token if multiple exist\nconst accessToken = await oauth.getAccessTokenForUser(userId, systemId, scopeId, 'google', {\n  autoRefresh: true,\n});\n\n// Execute with valid token for specific user\nawait oauth.withValidTokenForUser(userId, systemId, scopeId, 'google', async (accessToken) => {\n  // Make API calls with the token\n  return apiResponse;\n});\n\n// Get all valid tokens for a user with auto-refresh\nconst userTokens = await oauth.getAllValidTokensForUser(userId, {\n  autoRefresh: true,\n  refreshBuffer: 5, // Refresh 5 minutes before expiry\n});\n\n// Check if user has tokens for specific provider/scope\nconst hasToken = await oauth.hasTokenForUser(userId, systemId, scopeId, 'google');\n\n// Get user token entity (includes metadata)\nconst userToken = await oauth.getUserTokenForUser(userId, systemId, scopeId, 'google');\n\n// Revoke specific tokens\nawait oauth.revokeTokensForUser(userId, systemId, scopeId, 'google');\n```\n\n### PKCE Support\n\n```typescript\n// Enable PKCE for enhanced security\nconst { url, state } = await oauth.authorize({\n  provider: 'google',\n  usePKCE: true, // Enables PKCE flow\n});\n```\n\n### Token Validation\n\n```typescript\n// Check if token is expired\nconst isExpired = oauth.isTokenExpired(token, {\n  expirationBuffer: 60, // Consider expired 60 seconds early\n});\n\n// Ensure valid token (auto-refresh if needed)\nconst validToken = await oauth.ensureValidToken('google');\n```\n\n## 🔒 Security Features\n\n### State Management\n\n- Cryptographically secure state generation\n- Automatic state validation and cleanup\n- Configurable state expiration\n\n### PKCE (Proof Key for Code Exchange)\n\n- Built-in PKCE support for public clients\n- Automatic code verifier generation\n- Enhanced security for mobile and SPA applications\n\n### Token Encryption\n\n- Secure token storage with optional encryption\n- Configurable seal keys for sensitive data\n- Protection against token theft\n\n### Email Validation\n\n- Automatic email conflict detection\n- Profile-based user validation\n- Cross-provider email consistency\n\n## 🧪 Testing\n\nThe library includes comprehensive tests using Vitest:\n\n```bash\n# Run tests\nnpm test\n\n# Run tests with UI\nnpm run test:ui\n\n# Run tests with coverage\nnpm run test:coverage\n\n# Watch mode\nnpm run test:watch\n```\n\n## 🏢 Multi-System Examples\n\n### SaaS Platform with Multiple Apps\n\n```typescript\n// Create systems for different applications\nconst crmSystem = await oauth.createSystem('CRM App');\nconst analyticsSystem = await oauth.createSystem('Analytics Dashboard');\n\n// Create scopes for different access levels\nawait oauth.useSystem(crmSystem.id);\nconst readScope = await oauth.createScope('read-only', {\n  type: 'access',\n  permissions: ['read:contacts', 'read:deals'],\n  isolated: true,\n});\n\nconst adminScope = await oauth.createScope('admin', {\n  type: 'access',\n  permissions: ['*'],\n  isolated: true,\n});\n\n// Users can have different permissions per system\nconst user = await oauth.getOrCreateUser({ email: 'user@company.com' });\n\n// Authorize for specific system/scope\nconst { url } = await oauth.authorize({\n  provider: 'google',\n  scopes: ['profile', 'email'],\n});\n```\n\n### Multi-Tenant Application\n\n```typescript\n// Each tenant gets their own system\nconst tenantSystem = await oauth.createSystem(`Tenant-${tenantId}`);\n\n// Tenant-specific user management\nawait oauth.useSystem(tenantSystem.id);\nconst tenantUser = await oauth.getOrCreateUser({\n  email: userEmail,\n  metadata: { tenantId, role: 'admin' },\n});\n\n// Tenant-isolated tokens\nconst tokens = await oauth.granular.getTokensBySystem(tenantSystem.id);\n```\n\n## 🚀 Production Deployment\n\n### Environment Configuration\n\n```typescript\nconst oauth = new OAuth2Client({\n  storage: await PostgresStorageFactory.create({\n    host: process.env.DB_HOST,\n    port: parseInt(process.env.DB_PORT || '5432'),\n    username: process.env.DB_USER,\n    password: process.env.DB_PASSWORD,\n    database: process.env.DB_NAME,\n    ssl: {\n      rejectUnauthorized: process.env.NODE_ENV === 'production',\n    },\n    poolSize: 20,\n    logging: process.env.NODE_ENV === 'development',\n  }),\n  sealKey: process.env.OAUTH2_SEAL_KEY, // For token encryption\n  providers: {\n    google: {\n      clientId: process.env.GOOGLE_CLIENT_ID,\n      clientSecret: process.env.GOOGLE_CLIENT_SECRET,\n      redirectUri: process.env.GOOGLE_REDIRECT_URI,\n      // ... other config\n    },\n  },\n});\n```\n\n### Performance Optimization\n\n```typescript\n// Use token caching for high-traffic scenarios\nconst accessToken = await oauth.getAccessToken('google', {\n  autoRefresh: true,\n  refreshBuffer: 10, // Refresh early to avoid expiry\n});\n\n// Batch operations for efficiency\nconst allTokens = await oauth.getAllValidTokensForUser(userId);\n\n// Clean up expired states regularly\nsetInterval(\n  async () => {\n    await oauth.cleanup(10 * 60 * 1000); // 10 minutes\n  },\n  5 * 60 * 1000,\n); // Every 5 minutes\n```\n\n### Error Handling\n\n```typescript\ntry {\n  const token = await oauth.getAccessToken('google');\n} catch (error) {\n  if (error.message.includes('Token expired')) {\n    // Handle token expiry\n    const { url } = await oauth.authorize({ provider: 'google' });\n    // Redirect to re-authorization\n  } else if (error.message.includes('Provider not found')) {\n    // Handle missing provider\n  }\n}\n```\n\n## 🤝 Contributing\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add some amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n### Development Setup\n\n```bash\n# Install dependencies\nnpm install\n\n# Run in development mode\nnpm run dev\n\n# Run tests\nnpm test\n\n# Build the project\nnpm run build\n\n# Lint and format\nnpm run lint:fix\nnpm run format\n```\n\n## 📄 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## 🙋‍♂️ Support\n\n- 📚 [Documentation](https://github.com/blureffect/oauth2-token-manager#readme)\n- 🐛 [Issue Tracker](https://github.com/blureffect/oauth2-token-manager/issues)\n- 💬 [Discussions](https://github.com/blureffect/oauth2-token-manager/discussions)\n\n## 🏆 Credits\n\nCreated with ❤️ by [Blureffect](https://blureffect.co)\n","readmeFilename":"README.md"}