{"_id":"@autolabz/service-auth-fastify","_rev":"2-f42b4d68ad9f107c0c0058acc6e1157f","name":"@autolabz/service-auth-fastify","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@autolabz/service-auth-fastify","version":"1.0.0","_id":"@autolabz/service-auth-fastify@1.0.0","maintainers":[{"name":"autolabz","email":"mzhh@mzhh.xyz"}],"dist":{"shasum":"84cfdeee44a7e89d78b9fc93206fd59badac4849","tarball":"https://registry.npmjs.org/@autolabz/service-auth-fastify/-/service-auth-fastify-1.0.0.tgz","fileCount":8,"integrity":"sha512-ox3vflrZf+WanzbDJ0kLZdRoeKuUY3cKFlEl5Wnq8mQZP/+q5pTWO2QjmQcm+1ictXBTQixw0CmM70jATv1oyg==","signatures":[{"sig":"MEUCIELIprKyhg+eljOjGVVP/AQiNzU/Cs9HnyUk4hh7YrUTAiEApaajhbHISxkF872m/0td1T5tMhEZf0QDMDzQ46Fjlgc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":40360},"main":"dist/index.cjs","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"7aaed46abb7b0c16716e887d0501aadcd4b00fa6","scripts":{"build":"npm run clean && tsup src/index.ts --format esm,cjs --dts --sourcemap --clean --target node18","clean":"rimraf dist","prepare":"npm run build","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"autolabz","email":"mzhh@mzhh.xyz"},"_npmVersion":"10.8.2","description":"Fastify authentication middleware for AutoLab services","directories":{},"sideEffects":false,"_nodeVersion":"20.19.5","dependencies":{"fastify-plugin":"^4.5.1","@autolabz/service-auth-core":"file:../service-auth-core"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","rimraf":"^5.0.5","fastify":"^4.28.1","typescript":"^5.6.3"},"peerDependencies":{"fastify":"^4.28.1"},"_npmOperationalInternal":{"tmp":"tmp/service-auth-fastify_1.0.0_1763006906126_0.9439482627645166","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@autolabz/service-auth-fastify","version":"1.0.1","type":"module","description":"Fastify authentication middleware for AutoLab services","sideEffects":false,"main":"dist/index.cjs","module":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"peerDependencies":{"fastify":"^4.28.1"},"dependencies":{"@autolabz/service-auth-core":"^1.0.1","fastify-plugin":"^4.5.1"},"devDependencies":{"fastify":"^4.28.1","rimraf":"^5.0.5","tsup":"^8.3.0","typescript":"^5.6.3"},"scripts":{"clean":"rimraf dist","build":"npm run clean && tsup src/index.ts --format esm,cjs --dts --sourcemap --clean --target node18","prepare":"npm run build","typecheck":"tsc -p tsconfig.json --noEmit"},"_id":"@autolabz/service-auth-fastify@1.0.1","gitHead":"7aaed46abb7b0c16716e887d0501aadcd4b00fa6","_nodeVersion":"20.19.5","_npmVersion":"10.8.2","dist":{"integrity":"sha512-p9aauOIKqbuPmdHzn0a/lb3FpGiJCrRNuCk1TOitNcxVRU5qJGq35U5LWyQeqk3zAT+1TNx0S8BKhk+aKaBEyQ==","shasum":"6121fbb071a4cc4f40a5bfd089e1d8c06233a7e7","tarball":"https://registry.npmjs.org/@autolabz/service-auth-fastify/-/service-auth-fastify-1.0.1.tgz","fileCount":8,"unpackedSize":40341,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQC3RyHUiMfWCSWnECbRYPDzwq9dcgFzDRFre0DaBGt7lQIgTN8UQeCBqi1ZNMq27FR8KTuulpmpWGdWK0ay8BL883o="}]},"_npmUser":{"name":"autolabz","email":"mzhh@mzhh.xyz"},"directories":{},"maintainers":[{"name":"autolabz","email":"mzhh@mzhh.xyz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/service-auth-fastify_1.0.1_1763033921814_0.4527633755753453"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-13T04:08:26.013Z","modified":"2025-11-13T11:38:42.291Z","1.0.0":"2025-11-13T04:08:26.320Z","1.0.1":"2025-11-13T11:38:42.064Z"},"description":"Fastify authentication middleware for AutoLab services","maintainers":[{"name":"autolabz","email":"mzhh@mzhh.xyz"}],"readme":"# @autolabz/service-auth-fastify\n\nFastify authentication middleware for AutoLab services: centralized authentication with fallback to OAuth userinfo.\n\n> **Version 1.0.0** - This is a major version upgrade from `@autolabz/service-auth-middleware` v0.2.x with breaking changes. See [Migration from v0.2.x](#migration-from-v02x) below.\n\n## Features\n\n- **Unified authentication chain**: Prioritize SIMPLE JWT verification, fallback to OAuth userinfo\n- **OAuth validation**: \n  - X-Client-Id matches azp (or userinfo.client_id)\n  - Support for required scopes subset validation\n- **Flexible**: iss/aud validation only when JWT claims exist; skip during userinfo fallback\n- **Framework-agnostic core**: Built on `@autolabz/service-auth-core` for maximum reusability\n\n## Installation\n\n```bash\nnpm install @autolabz/service-auth-fastify\n```\n\n## Quick Start\n\nUse the all-in-one plugin for the simplest integration:\n\n```typescript\nimport Fastify from 'fastify';\nimport { authPlugin } from '@autolabz/service-auth-fastify';\n\nconst app = Fastify({ logger: true });\n\nconst authCfg = {\n  jwtAlg: 'HS256',\n  jwtAccessSecret: process.env.JWT_ACCESS_SECRET,\n  authBaseUrl: process.env.AUTH_BASE_URL!,\n  oauthUserinfoPath: '/oauth/userinfo',\n  oauthUserinfoTimeoutMs: 2000,\n};\n\napp.register(authPlugin, {\n  authConfig: authCfg,\n  clientId: {},\n  enforce: { requiredScopes: ['data'] }, // Optional: require 'data' scope\n});\n\napp.get('/api/hello', async (req, reply) => {\n  const userId = req.auth?.userId;\n  const clientId = req.clientId;\n  return { message: `Hello, user ${userId} from client ${clientId}` };\n});\n\napp.listen({ port: 3000, host: '0.0.0.0' });\n```\n\n## Usage with Downstream Services\n\nUse `makeAuthBridgeFromRequest` to create an AuthBridge that transparently forwards authentication to downstream services:\n\n```typescript\nimport { makeAuthBridgeFromRequest } from '@autolabz/service-auth-fastify';\nimport { createPointsClient } from '@autolabz/points-sdk';\nimport { createDataClient } from '@autolabz/data-sdk';\nimport { createLLMClient } from '@autolabz/llmapi-sdk';\n\napp.get('/api/user/balance', async (req, reply) => {\n  // Create AuthBridge from incoming request\n  const auth = makeAuthBridgeFromRequest(req, {\n    onUnauthorized: () => {\n      req.log.warn('Downstream service returned 401');\n    },\n  });\n\n  // Pass to SDK\n  const pointsClient = createPointsClient({\n    baseURL: process.env.POINTS_BASE_URL!,\n    auth,\n  });\n\n  const balance = await pointsClient.getMyBalance();\n  return reply.send(balance);\n});\n```\n\n## Configuration Reference\n\n### `AuthConfig`\n\n| Key | Description | Required | Default |\n| --- | --- | --- | --- |\n| `jwtAlg` | SIMPLE mode algorithm: 'HS256' or 'RS256' | Yes | - |\n| `jwtAccessSecret` | HS256 local verification secret | Yes (when jwtAlg=HS256) | - |\n| `jwksUrl` | RS256 JWK Set URL | Yes (when jwtAlg=RS256) | - |\n| `authIssuer` | Expected issuer (validates only when JWT claim exists) | No | - |\n| `authBaseUrl` | OAuth base URL | Yes | - |\n| `oauthUserinfoPath` | Userinfo endpoint path | No | 'oauth/userinfo' |\n| `oauthUserinfoTimeoutMs` | Userinfo request timeout (ms) | No | 2000 |\n| `oauthExpectedAudience` | Expected audience value | No | - |\n\n### `EnforceOptions`\n\n| Key | Description | Default |\n| --- | --- | --- |\n| `requiredScopes` | Array of required scopes (subset check) | `[]` (no check) |\n| `enforceForSimple` | Enforce scope check for SIMPLE mode | `false` |\n\n## Environment Variables\n\nTypical environment variable setup:\n\n```bash\n# SIMPLE mode (local JWT verification)\nJWT_ALG=HS256\nJWT_ACCESS_SECRET=your-secret\n\n# OAuth userinfo fallback\nAUTH_BASE_URL=http://auth-service:4001/api\nOAUTH_USERINFO_PATH=/oauth/userinfo\nOAUTH_USERINFO_TIMEOUT_MS=2000\n\n# Optional\nAUTH_ISSUER=https://auth.example.com\nOAUTH_EXPECTED_AUDIENCE=autolab-api\n```\n\n## Advanced Usage\n\n### Custom Scope Requirements per Route\n\n```typescript\nimport { oauthEnforceClientScope } from '@autolabz/service-auth-fastify';\n\napp.get('/api/admin/users', {\n  preHandler: oauthEnforceClientScope(authCfg, { requiredScopes: ['admin'] }),\n}, async (req, reply) => {\n  // This route requires 'admin' scope\n  return { users: [] };\n});\n```\n\n### Manual Middleware Chain\n\nIf you prefer manual control over the middleware chain:\n\n```typescript\nimport { \n  oauthOrSimpleAuth, \n  clientIdMiddleware, \n  oauthEnforceClientScope \n} from '@autolabz/service-auth-fastify';\n\napp.addHook('onRequest', oauthOrSimpleAuth(authCfg));\napp.addHook('onRequest', clientIdMiddleware({}));\napp.addHook('onRequest', oauthEnforceClientScope(authCfg, { requiredScopes: ['data'] }));\n```\n\n## Request Properties\n\nAfter authentication, the following properties are added to the request:\n\n- **`req.auth`**: `AuthPayload` object\n  - `userId`: User ID (UUID)\n  - `sub?`: Subject\n  - `email?`: User email\n  - `iss?`: Issuer\n  - `aud?`: Audience\n  - `azp?`: Authorized party (client ID)\n  - `scope?`: Token scopes (space-separated)\n  - `tokenType?`: Token type\n\n- **`req.clientId`**: Client ID from `X-Client-Id` header or `client_id` query parameter\n\n## Migration from v0.2.x\n\n### Breaking Changes\n\n1. **Package name changed**: `@autolabz/service-auth-middleware` → `@autolabz/service-auth-fastify`\n2. **Core logic extracted**: JWT/userinfo logic moved to `@autolabz/service-auth-core`\n3. **Plugin name updated**: Internal plugin name changed from `@autolabz/service-auth-middleware:authPlugin` to `@autolabz/service-auth-fastify:authPlugin`\n\n### Migration Steps\n\n1. Update `package.json`:\n\n```diff\n{\n  \"dependencies\": {\n-   \"@autolabz/service-auth-middleware\": \"^0.2.2\"\n+   \"@autolabz/service-auth-fastify\": \"^1.0.0\"\n  }\n}\n```\n\n2. Update imports:\n\n```diff\n- import { authPlugin } from '@autolabz/service-auth-middleware';\n+ import { authPlugin } from '@autolabz/service-auth-fastify';\n```\n\n3. No code changes required - the API remains the same!\n\n## Troubleshooting\n\n### 401 Unauthorized\n\n- Check `AUTH_BASE_URL` and `OAUTH_USERINFO_PATH` are correct\n- Verify `Authorization` header uses `Bearer <token>` format\n- Confirm token has required scopes\n\n### X-Client-Id Mismatch\n\n- Ensure gateway doesn't forward external `X-Client-Id` headers\n- Let backend set this header consistently\n\n### Request Timeout\n\n- Increase `OAUTH_USERINFO_TIMEOUT_MS`\n- Check auth service performance and network connectivity\n\n### JWT Verification Failed\n\n- **HS256**: Verify `JWT_ACCESS_SECRET` matches auth service\n- **RS256**: Ensure `JWKS_URL` is accessible and `kid` matches\n\n## License\n\nMIT\n","readmeFilename":"README.md"}