{"_id":"@drvalue-oss/iam-nestjs","_rev":"11-e2ce9e6f49c661dfb1e443a98b30ab4b","name":"@drvalue-oss/iam-nestjs","dist-tags":{"latest":"0.9.0"},"versions":{"0.1.0":{"name":"@drvalue-oss/iam-nestjs","version":"0.1.0","keywords":["drvalue","iam","nestjs","auth","jwt","guard"],"license":"MIT","_id":"@drvalue-oss/iam-nestjs@0.1.0","maintainers":[{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"}],"homepage":"https://github.com/drvalue/drvalue-iam-js/tree/main/packages/iam-nestjs#readme","bugs":{"url":"https://github.com/drvalue/drvalue-iam-js/issues"},"dist":{"shasum":"5085d40b58433a19c1e59a820227b6a781324ae1","tarball":"https://registry.npmjs.org/@drvalue-oss/iam-nestjs/-/iam-nestjs-0.1.0.tgz","fileCount":9,"integrity":"sha512-q+CSKM9RWFlctWLjJevdpbSxv5U8NkRFlkJtKkDGNVCvJ7PMERevk0iyIyHYtM4P4bRxynEwLKWFgMjHz6FQJw==","signatures":[{"sig":"MEUCIAZi7kVSE1uzky6PPaOBV/4UY7/GneS4r2nbcWEVyy1oAiEA89rZWcqqQSuzch/Xv8b28pGN94/NSYV7SS0Z0DFZZsM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":102063},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20.19"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"1c7de87d7d251119411f19eec90b8da8d9af4e24","scripts":{"dev":"tsup --watch","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"},"repository":{"url":"git+https://github.com/drvalue/drvalue-iam-js.git","type":"git","directory":"packages/iam-nestjs"},"_npmVersion":"10.9.7","description":"NestJS module for drvalue IAM: gateway signature verification, X-User-* parsing, decorators, proxy helpers","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"@drvalue-oss/iam-core":"workspace:*"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.1","tsup":"^8.3.5","@swc/core":"^1.10.1","typescript":"^5.7.2","@types/node":"^22.10.2","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/iam-nestjs_0.1.0_1778927188376_0.5645990889462076","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@drvalue-oss/iam-nestjs","version":"0.1.1","keywords":["drvalue","iam","nestjs","auth","jwt","guard"],"license":"MIT","_id":"@drvalue-oss/iam-nestjs@0.1.1","maintainers":[{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"}],"dist":{"shasum":"a6356514abdf50c6a404e823c85cdd81e3e9910c","tarball":"https://registry.npmjs.org/@drvalue-oss/iam-nestjs/-/iam-nestjs-0.1.1.tgz","fileCount":9,"integrity":"sha512-esPjiWTCDZmtOr0L4ERXKsqFdk59rRAOo8aEUh9vCqQzq8j6W1C7UqAKFC4b95vSEmrdBEAUZFXlOxJHqPIj8g==","signatures":[{"sig":"MEUCIGOnV6OIBF9Qm0HNwp64Mtr3UWkJHzb17vYybUkCWzyPAiEAq6kBncIUrMmj4cpRNdZYi+j1t2+EbzboMkEzOL9TMt8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":101850},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20.19"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"758fea7dc5ce2eaccbbe7091ee93990b6ae402e4","scripts":{"dev":"tsup --watch","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"},"_npmVersion":"10.9.7","description":"NestJS module for drvalue IAM: gateway signature verification, X-User-* parsing, decorators, proxy helpers","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"@drvalue-oss/iam-core":"workspace:*"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.1","tsup":"^8.3.5","@swc/core":"^1.10.1","typescript":"^5.7.2","@types/node":"^22.10.2","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/iam-nestjs_0.1.1_1779436842251_0.3233149593681188","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@drvalue-oss/iam-nestjs","version":"0.1.2","keywords":["drvalue","iam","nestjs","auth","jwt","guard"],"license":"MIT","_id":"@drvalue-oss/iam-nestjs@0.1.2","maintainers":[{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"}],"dist":{"shasum":"43de8c3a9416e0d46609dced283542e9377c7cdc","tarball":"https://registry.npmjs.org/@drvalue-oss/iam-nestjs/-/iam-nestjs-0.1.2.tgz","fileCount":9,"integrity":"sha512-KNI8Xm/88/TQG+AXHYOrrBSR0xPMK3DRR3KJHUZJ4gDz5hMlXeZZeZaHMVwirWt0sZ4Al4NNvUEYODmAiE1K7g==","signatures":[{"sig":"MEUCICTQYVB8TRhM9NWrSZWK7GOhN0MM33NvaW03b80hQjNrAiEAgBLfCDSFPrymalG9TL0FwVJpYdd7CcijI5LDzqpEv5I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":101843},"main":"./dist/index.cjs","type":"module","_from":"file:drvalue-oss-iam-nestjs-0.1.2.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20.19"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"dev":"tsup --watch","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"},"_resolved":"/tmp/claude-1001/4d561c6372cd10f3fc4fc619af831456/drvalue-oss-iam-nestjs-0.1.2.tgz","_integrity":"sha512-KNI8Xm/88/TQG+AXHYOrrBSR0xPMK3DRR3KJHUZJ4gDz5hMlXeZZeZaHMVwirWt0sZ4Al4NNvUEYODmAiE1K7g==","_npmVersion":"10.9.7","description":"NestJS module for drvalue IAM: gateway signature verification, X-User-* parsing, decorators, proxy helpers","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"@drvalue-oss/iam-core":"0.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.1","tsup":"^8.3.5","@swc/core":"^1.10.1","typescript":"^5.7.2","@types/node":"^22.10.2","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/iam-nestjs_0.1.2_1781058644864_0.4932327328679491","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@drvalue-oss/iam-nestjs","version":"0.2.0","keywords":["drvalue","iam","nestjs","auth","jwt","guard"],"license":"MIT","_id":"@drvalue-oss/iam-nestjs@0.2.0","maintainers":[{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"}],"dist":{"shasum":"4729e7086d1c95a278dbcb2fe6ae1803225b7afa","tarball":"https://registry.npmjs.org/@drvalue-oss/iam-nestjs/-/iam-nestjs-0.2.0.tgz","fileCount":9,"integrity":"sha512-FGg9jbn/aJA8JkrZJxxjUYOryTGJTBXMFtOmHdxI51zu33PZxp6hkF1tr5PH4T+rGUIiv94hmK6aeigwKFmZFQ==","signatures":[{"sig":"MEYCIQDe3teBXEQHLZbN/CNNkkb4IfM70DC8VHMmenn//c5wzwIhAKYGXjDBqwpPzUDpkEBo0/pjqG/d9qQwQWHQ5sjqk/cA","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":134676},"main":"./dist/index.cjs","type":"module","_from":"file:drvalue-oss-iam-nestjs-0.2.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20.19"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"},"_resolved":"/tmp/claude-1001/18845229826bf7b67cdd8bce4648b6bb/drvalue-oss-iam-nestjs-0.2.0.tgz","_integrity":"sha512-FGg9jbn/aJA8JkrZJxxjUYOryTGJTBXMFtOmHdxI51zu33PZxp6hkF1tr5PH4T+rGUIiv94hmK6aeigwKFmZFQ==","_npmVersion":"10.9.7","description":"NestJS module for drvalue IAM: gateway signature verification, X-User-* parsing, decorators, proxy helpers","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"@drvalue-oss/iam-core":"0.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.1","tsup":"^8.3.5","vitest":"^4.1.8","@swc/core":"^1.10.1","typescript":"^5.7.2","@types/node":"^22.10.2","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/iam-nestjs_0.2.0_1781064581156_0.8614448506659365","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@drvalue-oss/iam-nestjs","version":"0.4.0","keywords":["drvalue","iam","nestjs","auth","jwt","guard"],"license":"MIT","_id":"@drvalue-oss/iam-nestjs@0.4.0","maintainers":[{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"}],"dist":{"shasum":"3e78e36b65203db64c47aa6f22cfb9868624fde8","tarball":"https://registry.npmjs.org/@drvalue-oss/iam-nestjs/-/iam-nestjs-0.4.0.tgz","fileCount":9,"integrity":"sha512-/SN/6jG/7kXnPfgU9XEJmks3+DxQo6oWPr20t8jOmmYLb5gdRZfdZeV1x5wnRrw9OO1a4F1kzur95bDyO6XkWg==","signatures":[{"sig":"MEUCIQC2thlcvgqJfGhGtEuDz9OxXZFOSYuLPMx97hrIzfrg3AIgQ3ONWwwlCPwnOzKI+3eFlhCeId5In7crXsBXPUTFr2A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":179736},"main":"./dist/index.cjs","type":"module","_from":"file:drvalue-oss-iam-nestjs-0.4.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20.19"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"},"_resolved":"/tmp/7f0a77fe71dc1872b9352db27711d6f6/drvalue-oss-iam-nestjs-0.4.0.tgz","_integrity":"sha512-/SN/6jG/7kXnPfgU9XEJmks3+DxQo6oWPr20t8jOmmYLb5gdRZfdZeV1x5wnRrw9OO1a4F1kzur95bDyO6XkWg==","_npmVersion":"10.9.7","description":"NestJS module for drvalue IAM: gateway signature verification, X-User-* parsing, decorators, proxy helpers","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"@drvalue-oss/iam-core":"0.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.1","tsup":"^8.3.5","vitest":"^4.1.8","@swc/core":"^1.10.1","typescript":"^5.7.2","@types/node":"^22.10.2","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/iam-nestjs_0.4.0_1781079033769_0.3875495201742123","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@drvalue-oss/iam-nestjs","version":"0.5.0","keywords":["drvalue","iam","nestjs","auth","jwt","guard"],"license":"MIT","_id":"@drvalue-oss/iam-nestjs@0.5.0","maintainers":[{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"}],"dist":{"shasum":"63f25a56df0bddc67cf7f03d2dff750fb93203ae","tarball":"https://registry.npmjs.org/@drvalue-oss/iam-nestjs/-/iam-nestjs-0.5.0.tgz","fileCount":9,"integrity":"sha512-7mm1pWtBvaQZT1bP6XVhNho6yRZJkpW6Mxa99/hn8uDvv0TbofTfywX5IU/79Zk266xnzksdGYmzcTCqMWAwOg==","signatures":[{"sig":"MEUCIEmOvP2U098Jgz2oe2PTqFLFKC28qogyGKWX1M3VZOdwAiEAqOUs57BiWoRIJskkVG0nC4LOPPCg3AfxrBPFqTHpaVY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":206934},"main":"./dist/index.cjs","type":"module","_from":"file:drvalue-oss-iam-nestjs-0.5.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20.19"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"},"_resolved":"/tmp/5d9168e69593ab068cede081fa036d72/drvalue-oss-iam-nestjs-0.5.0.tgz","_integrity":"sha512-7mm1pWtBvaQZT1bP6XVhNho6yRZJkpW6Mxa99/hn8uDvv0TbofTfywX5IU/79Zk266xnzksdGYmzcTCqMWAwOg==","_npmVersion":"10.9.7","description":"NestJS module for drvalue IAM: gateway signature verification, X-User-* parsing, decorators, proxy helpers","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"@drvalue-oss/iam-core":"0.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.1","tsup":"^8.3.5","vitest":"^4.1.8","@swc/core":"^1.10.1","typescript":"^5.7.2","@types/node":"^22.10.2","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/iam-nestjs_0.5.0_1781085040677_0.1653358423402187","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@drvalue-oss/iam-nestjs","version":"0.6.0","keywords":["drvalue","iam","nestjs","auth","jwt","guard"],"license":"MIT","_id":"@drvalue-oss/iam-nestjs@0.6.0","maintainers":[{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"}],"dist":{"shasum":"9040be45927dfecf9f9f8eb84e2d93a0b1169c1f","tarball":"https://registry.npmjs.org/@drvalue-oss/iam-nestjs/-/iam-nestjs-0.6.0.tgz","fileCount":9,"integrity":"sha512-NXkmoAkbyKiYtwPm+9LKEoUFKYiKkGh2pjuuGb6nfGrQ4xMn/wDCVqMRPNibFcwqdPJpUM0ZSXJkinQQoNLhEA==","signatures":[{"sig":"MEQCICKKSHmQ1O13ALLqj4J12N+ta1G0K6cSq4B9dmZxW7bFAiBa/6Y/RhBW7n+pQzcInEkF5hrJkMRYLZcHm6mrHagnig==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":219703},"main":"./dist/index.cjs","type":"module","_from":"file:drvalue-oss-iam-nestjs-0.6.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20.19"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"},"_resolved":"/tmp/014e691fa6732e6ae84a29f58949035e/drvalue-oss-iam-nestjs-0.6.0.tgz","_integrity":"sha512-NXkmoAkbyKiYtwPm+9LKEoUFKYiKkGh2pjuuGb6nfGrQ4xMn/wDCVqMRPNibFcwqdPJpUM0ZSXJkinQQoNLhEA==","_npmVersion":"10.9.7","description":"NestJS module for drvalue IAM: gateway signature verification, X-User-* parsing, decorators, proxy helpers","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"@drvalue-oss/iam-core":"0.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.1","tsup":"^8.3.5","vitest":"^4.1.8","@swc/core":"^1.10.1","typescript":"^5.7.2","@types/node":"^22.10.2","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/iam-nestjs_0.6.0_1781229894079_0.7788399461015643","host":"s3://npm-registry-packages-npm-production"}},"0.6.1":{"name":"@drvalue-oss/iam-nestjs","version":"0.6.1","keywords":["drvalue","iam","nestjs","auth","jwt","guard"],"license":"MIT","_id":"@drvalue-oss/iam-nestjs@0.6.1","maintainers":[{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"}],"dist":{"shasum":"32a3b4e0b119b727a08571564cdfa9ed9d9e91e9","tarball":"https://registry.npmjs.org/@drvalue-oss/iam-nestjs/-/iam-nestjs-0.6.1.tgz","fileCount":9,"integrity":"sha512-oUj3Nq8iNGTlJwFJiUtza6XIFlbY9Ba0zoFoojtLimF/HzSpG9RiFXr+IcMXhakd7gIeXUQ0H2Qm1uhsjD9FUQ==","signatures":[{"sig":"MEYCIQCAS2pdWWvlowd/I58ZgZmllPR2xIUT978H2qrZREp02gIhALFsfe9kjKCLtkrXAdfqf5PVXfkhmXWH+uBh9RNmOwBQ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":222380},"main":"./dist/index.cjs","type":"module","_from":"file:drvalue-oss-iam-nestjs-0.6.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20.19"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"},"_resolved":"/tmp/d1b58c32ff4447fbc04741d24152bae0/drvalue-oss-iam-nestjs-0.6.1.tgz","_integrity":"sha512-oUj3Nq8iNGTlJwFJiUtza6XIFlbY9Ba0zoFoojtLimF/HzSpG9RiFXr+IcMXhakd7gIeXUQ0H2Qm1uhsjD9FUQ==","_npmVersion":"10.9.7","description":"NestJS module for drvalue IAM: gateway signature verification, X-User-* parsing, decorators, proxy helpers","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"@drvalue-oss/iam-core":"0.2.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.1","tsup":"^8.3.5","vitest":"^4.1.8","@swc/core":"^1.10.1","typescript":"^5.7.2","@types/node":"^22.10.2","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/iam-nestjs_0.6.1_1781238801561_0.32250665916362187","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"name":"@drvalue-oss/iam-nestjs","version":"0.7.0","keywords":["drvalue","iam","nestjs","auth","jwt","guard"],"license":"MIT","_id":"@drvalue-oss/iam-nestjs@0.7.0","maintainers":[{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"}],"dist":{"shasum":"28174a662494684d5be0f5371cef6850b708795d","tarball":"https://registry.npmjs.org/@drvalue-oss/iam-nestjs/-/iam-nestjs-0.7.0.tgz","fileCount":9,"integrity":"sha512-/ywyHSQiV1yrm1bKBR/YtpkIxxMcqV/K02ilyE4EyiD2sX1WDajtux3CIBB9pjxjZb7rAQHts692Vzdb1qBDpQ==","signatures":[{"sig":"MEUCIQDPRW+oxVbHLI9QudX+RWCmhH71VD45qabf82lXozRmpgIgTir6Ma9m6wY85HGkSwWS3EBR1hk7WgNBqPMr7uvI2Bg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":296985},"main":"./dist/index.cjs","type":"module","_from":"file:drvalue-oss-iam-nestjs-0.7.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20.19"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"},"_resolved":"/tmp/2ee214a9707eccb2d688504f374c645f/drvalue-oss-iam-nestjs-0.7.0.tgz","_integrity":"sha512-/ywyHSQiV1yrm1bKBR/YtpkIxxMcqV/K02ilyE4EyiD2sX1WDajtux3CIBB9pjxjZb7rAQHts692Vzdb1qBDpQ==","_npmVersion":"10.9.7","description":"NestJS module for drvalue IAM: gateway signature verification, X-User-* parsing, decorators, proxy helpers, outbound subscription-mirror client","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"@drvalue-oss/iam-core":"0.2.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.1","tsup":"^8.3.5","vitest":"^4.1.8","@swc/core":"^1.10.1","typescript":"^5.7.2","@types/node":"^22.10.2","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/iam-nestjs_0.7.0_1781256113040_0.5528467686470013","host":"s3://npm-registry-packages-npm-production"}},"0.8.0":{"name":"@drvalue-oss/iam-nestjs","version":"0.8.0","keywords":["drvalue","iam","nestjs","auth","jwt","guard"],"license":"MIT","_id":"@drvalue-oss/iam-nestjs@0.8.0","maintainers":[{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"}],"dist":{"shasum":"c9a0197e05c6c6bd44ab3aa4c0e1dacc25d73e6b","tarball":"https://registry.npmjs.org/@drvalue-oss/iam-nestjs/-/iam-nestjs-0.8.0.tgz","fileCount":9,"integrity":"sha512-nodYucLI50M7g6E9xYfKHqaXKmqB6LYaTUeHvy5d+q0ssMz8wcf6rILn7SDtZSlYIPEcymZDcxtG2+jr7aZTsA==","signatures":[{"sig":"MEUCIH6RmnSDK8QKRG+jwQgJsPcUaLOskwfbLVL09Z6jFlDmAiEAyXYBiXWNBxcxX+qYeAxMv0vdXfsJEtGechby7voq4vE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":328129},"main":"./dist/index.cjs","type":"module","_from":"file:drvalue-oss-iam-nestjs-0.8.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=20.19"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"},"_resolved":"/tmp/42e9eb9e63413ca9cf2295be25152ed3/drvalue-oss-iam-nestjs-0.8.0.tgz","_integrity":"sha512-nodYucLI50M7g6E9xYfKHqaXKmqB6LYaTUeHvy5d+q0ssMz8wcf6rILn7SDtZSlYIPEcymZDcxtG2+jr7aZTsA==","_npmVersion":"10.9.7","description":"NestJS module for drvalue IAM: gateway signature verification, X-User-* parsing, decorators, proxy helpers, outbound subscription-mirror client","directories":{},"sideEffects":false,"_nodeVersion":"22.22.2","dependencies":{"@drvalue-oss/iam-core":"0.8.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.1","tsup":"^8.3.5","vitest":"^4.1.8","@swc/core":"^1.10.1","typescript":"^5.7.2","@types/node":"^22.10.2","@nestjs/core":"^11.0.0","@nestjs/common":"^11.0.0","reflect-metadata":"^0.2.2"},"peerDependencies":{"rxjs":"^7.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0","reflect-metadata":"^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/iam-nestjs_0.8.0_1781269680549_0.46459139588303744","host":"s3://npm-registry-packages-npm-production"}},"0.9.0":{"name":"@drvalue-oss/iam-nestjs","version":"0.9.0","description":"NestJS module for drvalue IAM: gateway signature verification, X-User-* parsing, decorators, proxy helpers, outbound subscription-mirror and user-lookup clients","license":"MIT","type":"module","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"}},"publishConfig":{"access":"public"},"sideEffects":false,"keywords":["drvalue","iam","nestjs","auth","jwt","guard"],"dependencies":{"@drvalue-oss/iam-core":"0.8.0"},"peerDependencies":{"@nestjs/common":"^10.0.0 || ^11.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","reflect-metadata":"^0.2.0","rxjs":"^7.0.0"},"devDependencies":{"@nestjs/common":"^11.0.0","@nestjs/core":"^11.0.0","@swc/core":"^1.10.1","@types/node":"^22.10.2","reflect-metadata":"^0.2.2","rxjs":"^7.8.1","tsup":"^8.3.5","typescript":"^5.7.2","vitest":"^4.1.8"},"engines":{"node":">=20.19"},"scripts":{"build":"tsup","dev":"tsup --watch","clean":"rm -rf dist","typecheck":"tsc --noEmit","test":"vitest run"},"_id":"@drvalue-oss/iam-nestjs@0.9.0","_integrity":"sha512-XNc7ROf6WjsWoiYeml67Lo12z5vDiLz4zBQn4vMVQdtJx6L0lLD7/Wf6tMT5UVTPfDwif5Wcw+utkAaEo75LLQ==","_resolved":"/tmp/5725551d4cbe89ad737f221138d5964d/drvalue-oss-iam-nestjs-0.9.0.tgz","_from":"file:drvalue-oss-iam-nestjs-0.9.0.tgz","_nodeVersion":"22.22.2","_npmVersion":"10.9.7","dist":{"integrity":"sha512-XNc7ROf6WjsWoiYeml67Lo12z5vDiLz4zBQn4vMVQdtJx6L0lLD7/Wf6tMT5UVTPfDwif5Wcw+utkAaEo75LLQ==","shasum":"14aeb9d1a17a0d63188b3d36701cd6a7c7dde1be","tarball":"https://registry.npmjs.org/@drvalue-oss/iam-nestjs/-/iam-nestjs-0.9.0.tgz","fileCount":9,"unpackedSize":359471,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHb5LpVLkP0Qn75VAIckmsM8lrdtRHfIzZhNYkcn3VZpAiEAjaI5ws508c8LVD/yQWUsrOYBCKbdxfcQzvFA90Bg1RM="}]},"_npmUser":{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"},"directories":{},"maintainers":[{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/iam-nestjs_0.9.0_1781515387206_0.19583950230947877"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-16T10:26:28.228Z","modified":"2026-06-15T09:23:07.474Z","0.1.0":"2026-05-16T10:26:28.511Z","0.1.1":"2026-05-22T08:00:42.432Z","0.1.2":"2026-06-10T02:30:45.008Z","0.2.0":"2026-06-10T04:09:41.359Z","0.4.0":"2026-06-10T08:10:33.917Z","0.5.0":"2026-06-10T09:50:40.831Z","0.6.0":"2026-06-12T02:04:54.230Z","0.6.1":"2026-06-12T04:33:21.704Z","0.7.0":"2026-06-12T09:21:53.225Z","0.8.0":"2026-06-12T13:08:00.725Z","0.9.0":"2026-06-15T09:23:07.341Z"},"license":"MIT","keywords":["drvalue","iam","nestjs","auth","jwt","guard"],"description":"NestJS module for drvalue IAM: gateway signature verification, X-User-* parsing, decorators, proxy helpers, outbound subscription-mirror and user-lookup clients","maintainers":[{"name":"fkdldkrhya","email":"fkdldkrhya@hanyang.ac.kr"}],"readme":"# @drvalue-oss/iam-nestjs\n\nNestJS module for the drvalue IAM platform. Handles:\n\n- **HMAC-SHA256 gateway signature verification** — `GatewaySignatureGuard`, or `createGatewayAuthMiddleware()` / `verifyGatewayRequest()` for middleware/proxy paths where guards can't run\n- **`X-User-*` header parsing** — populates a typed `req.user: IamUserPayload`\n- **`@CurrentUser()`, `@CurrentGroup()`, `@Roles()`, `@Public()`, `@Authenticated()`, `@SkipGatewaySignature()`, `@RequireGatewaySignature()`** decorators\n- **Proxy helpers** — `forwardIamUserHeaders()` (one-call strip + re-inject), plus the lower-level `stripClientUserHeaders()` / `injectIamUserHeaders()`\n\n## Install\n\n```bash\npnpm add @drvalue-oss/iam-nestjs @drvalue-oss/iam-core\n```\n\nPeer dependencies: `@nestjs/common`, `@nestjs/core`, `reflect-metadata`, `rxjs`.\n\n## Setup\n\n```ts\n// app.module.ts\nimport { Module } from '@nestjs/common';\nimport { IamModule } from '@drvalue-oss/iam-nestjs';\n\n@Module({\n  imports: [\n    IamModule.forRoot({\n      // Required when enforceGatewayOnly=true. Same secret IAM Gateway uses to sign.\n      gatewaySharedSecret: process.env.GATEWAY_SHARED_SECRET!,\n\n      // PRODUCTION: must be true. Rejects requests without a valid signature.\n      // DEV: false lets you `curl localhost:3000` directly.\n      enforceGatewayOnly: process.env.NODE_ENV === 'production',\n\n      // Optional. Default ±30,000 ms.\n      signatureTimestampSkewMs: 30_000,\n\n      // Optional. Default true = every route requires an authenticated user\n      // unless marked @Public(). Set false to make routes public by default\n      // and opt in with @Authenticated() / @Roles().\n      secureByDefault: true,\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n`IamModule.forRoot()` installs both guards as `APP_GUARD`-scoped globals by default, so every controller is protected without `@UseGuards()`. Pass `global: false` if you want to apply them selectively.\n\nThe guards cover two independent axes. Each has a module-level default plus a force-ON and a force-OFF decorator. Method-level decorators override class-level; on a same-level conflict the safe side (protect/require) wins.\n\n| Axis | Module default | Force ON | Force OFF |\n|---|---|---|---|\n| **User auth** (`IamUserGuard`) | `secureByDefault` (default `true`) | `@Authenticated()` / `@Roles()` | `@Public()` |\n| **Gateway signature** (`GatewaySignatureGuard`) | `enforceGatewayOnly` | `@RequireGatewaySignature()` | `@SkipGatewaySignature()` |\n\n`@RequireGatewaySignature()` forces signature verification on one route even when `enforceGatewayOnly` is `false` (e.g. a sensitive endpoint while local dev is otherwise relaxed); if it is required but no `gatewaySharedSecret` is configured, the request is rejected (fail closed).\n\n## Using decorators\n\n```ts\nimport { Controller, Get } from '@nestjs/common';\nimport {\n  CurrentUser,\n  CurrentGroup,\n  Roles,\n  Public,\n  Authenticated,\n  SkipGatewaySignature,\n  RequireGatewaySignature,\n  type IamUserPayload,\n  type GroupMembership,\n} from '@drvalue-oss/iam-nestjs';\n\n@Controller('orders')\nexport class OrdersController {\n  @Get('mine')\n  @Roles('USER') // PLATFORM_ADMIN bypasses\n  list(@CurrentUser() user: IamUserPayload, @CurrentGroup() group: GroupMembership) {\n    return { userId: user.sub, groupId: group.id, role: group.role };\n  }\n\n  @Get('me')\n  @Authenticated() // any logged-in user (only needed when secureByDefault: false)\n  me(@CurrentUser() user: IamUserPayload) {\n    return { userId: user.sub };\n  }\n\n  @Get('public-stats')\n  @Public() // Skip IamUserGuard (signature still required)\n  stats() {\n    return { ok: true };\n  }\n\n  @Get('settle')\n  @Roles('ADMIN')\n  @RequireGatewaySignature() // always require gateway origin, even in dev\n  settle() {\n    return { settled: true };\n  }\n}\n\n@Controller('health')\nexport class HealthController {\n  @Get()\n  @SkipGatewaySignature()\n  @Public()\n  health() {\n    return { status: 'ok' };\n  }\n}\n```\n\n## Acting as a further proxy (BFF / gateway)\n\n`http-proxy-middleware` runs as **middleware**, which executes *before* NestJS\nguards — so on a proxied route the guards never run and `req.user` is never set.\nVerify the gateway signature and populate `req.user` yourself with\n`createGatewayAuthMiddleware()`, then re-inject the user with `forwardIamUserHeaders()`:\n\n```ts\n// app.module.ts\nimport { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';\nimport { IamModule, createGatewayAuthMiddleware, forwardIamUserHeaders } from '@drvalue-oss/iam-nestjs';\nimport { createProxyMiddleware } from 'http-proxy-middleware';\n\nconst apiUserProxy = createProxyMiddleware({\n  target: 'http://api-user:3001',\n  changeOrigin: true,\n  // NOTE: the forwarded request keeps the original gateway signature, which no\n  // longer verifies downstream. If the downstream runs `enforceGatewayOnly: true`,\n  // re-sign for the next hop — see \"Re-signing for a second hop\" below:\n  //   forwardIamUserHeaders(proxyReq, req, { resign: { secret: process.env.DOWNSTREAM_GATEWAY_SECRET! } })\n  on: { proxyReq: (proxyReq, req) => forwardIamUserHeaders(proxyReq, req) },\n});\n\n@Module({ imports: [IamModule.forRoot({ gatewaySharedSecret: process.env.GATEWAY_SHARED_SECRET!, global: false })] })\nexport class AppModule implements NestModule {\n  configure(consumer: MiddlewareConsumer) {\n    consumer\n      .apply(\n        // verify gateway origin (HMAC binds all X-User-* identity headers) + set req.user, then proxy\n        createGatewayAuthMiddleware({ secret: process.env.GATEWAY_SHARED_SECRET! }),\n        apiUserProxy,\n      )\n      .forRoutes('api-user');\n  }\n}\n```\n\n> `createGatewayAuthMiddleware` rejects unsigned/forged requests with `403` and is\n> the same HMAC check `GatewaySignatureGuard` does — done at the middleware layer\n> where guards can't reach. Need just the primitives? `verifyGatewayRequest(req, secret)`\n> returns `{ ok, reason, user }`, and `verifyGatewaySignature(params)` is the pure core\n> (the canonical binds timestamp, method, path, and all seven `X-User-*` headers).\n>\n> If you instead run the proxy from inside a **guarded controller** (`@All('*')`),\n> the guards already populate `req.user` — there `forwardIamUserHeaders(proxyReq, req)`\n> alone is enough.\n\n> **Heads-up:** the request forwarded this way still carries the original gateway\n> signature, but it will no longer verify downstream — `X-User-Groups-Detail` is\n> stripped without being re-injected, and the signed path/timestamp are bound to\n> this hop. Downstream services must either run with signature verification off\n> behind a network policy, or have this BFF re-sign — see\n> [Re-signing for a second hop](#re-signing-for-a-second-hop-bff--downstream).\n\n### Verifying only some paths (`path.include`)\n\nWhen the middleware is mounted broadly (a global `app.use(...)`, or a proxy in\nfront of NestJS routing) you may want it to verify only a subset of paths and\nlet the rest through untouched. Pass `path.include` — a whitelist of matchers:\n\n```ts\napp.use(\n  createGatewayAuthMiddleware({\n    secret: process.env.GATEWAY_SHARED_SECRET!,\n    // Only these paths are verified; everything else is passed straight through.\n    path: { include: ['/login/root/iam', '/api/**'] },\n  }),\n);\n```\n\nA request whose path matches **any** entry is verified exactly as before\n(`403` on a bad/missing signature). A request that matches **none** is passed\nstraight to `next()` — not verified, with `req.user` / `req.iamGatewayVerified`\nleft unset. Each matcher is a glob string (`*` = one non-slash segment, `**` =\nany run including slashes), a `RegExp`, or a `(path) => boolean` predicate.\nOmit `path.include` to verify every request (the default).\n\n> **Security:** `include` makes the *unmatched* paths unprotected by this\n> middleware. Only narrow it for routes you intend to leave open — if you are\n> registering through NestJS's `MiddlewareConsumer`, prefer `.forRoutes()` /\n> `.exclude()` so route scoping lives in one place.\n\n## Re-signing for a second hop (BFF → downstream)\n\nThe gateway signature binds the request **path**, so once a BFF proxies onward\n(path rewrite, header re-injection, timestamp aging) the original signature can\nnever verify downstream.\nEither disable signature verification downstream and rely on a network policy,\nor have the BFF **re-sign** for the next hop so the downstream service keeps\n`enforceGatewayOnly: true`:\n\n```ts\nconst downstreamProxy = createProxyMiddleware({\n  target: 'http://auth-svc:3001',\n  changeOrigin: true,\n  on: {\n    proxyReq: (proxyReq, req) =>\n      forwardIamUserHeaders(proxyReq, req, {\n        resign: { secret: process.env.DOWNSTREAM_GATEWAY_SECRET! },\n      }),\n  },\n});\n\napp.use(\n  '/auth',\n  createGatewayAuthMiddleware({ secret: process.env.GATEWAY_SHARED_SECRET! }),\n  downstreamProxy,\n);\n```\n\nWith `resign` set, `forwardIamUserHeaders`:\n\n1. strips client `X-User-*` headers and re-injects the verified user (as before),\n2. unconditionally drops (even when the request is unverified) the inbound `X-Gateway-Signature` / `X-Gateway-Timestamp`\n   (they are bound to the previous hop's path — dead weight downstream), and\n3. signs the outgoing request against the path the downstream service will\n   see — **only when `req.iamGatewayVerified === true`**, i.e. when\n   `createGatewayAuthMiddleware` (or `GatewaySignatureGuard`) cryptographically\n   verified the inbound signature. Unverified requests go out unsigned and are\n   rejected by the downstream's `enforceGatewayOnly` (fail closed).\n\nNotes:\n\n- A verified **anonymous** request is re-signed too (the gateway signs those\n  with all identity values empty) — `req.user` presence is deliberately NOT\n  the re-sign condition.\n- Prefer a **different secret per hop**: if a downstream service leaks its\n  secret, the attacker still cannot forge requests into the edge. An empty\n  `resign.secret` throws.\n- `X-User-Groups-Detail` is not re-injected by `forwardIamUserHeaders`, so it\n  is signed as empty downstream; parse it at the BFF if you need it.\n- `X-User-Active-Group` and `X-User-Phone` are **never** signature-protected,\n  on any hop — do not make authorization decisions from them.\n- Re-signing mints a **fresh timestamp**, so the downstream ±30s skew window\n  re-anchors at the BFF; like the gateway scheme itself there is no nonce, so\n  pair it with TLS / a trusted network between hops.\n- Low-level primitive: `signGatewayRequest(proxyReq, { secret, method?, path?, now? })`\n  signs any outgoing request whose headers are final.\n\n## Pushing subscription state to IAM (outbound)\n\nEverything above is **inbound** (verifying requests from IAM Gateway). The module\nalso ships an **outbound** client, `IamSubscriptionService`, so a product backend\ncan mirror subscription state into IAM from its own billing/webhook handlers. IAM\nrenders that mirror on the user's \"내 구독 관리\" portal and the admin dashboard.\n\nIt calls IAM's machine-to-machine API\n(`PUT`/`DELETE /internal/api/products/{productSlug}/subscriptions/{userId}`),\nauthenticated with `X-Internal-Api-Key`. Configure it in `forRoot()`:\n\n```ts\nIamModule.forRoot({\n  gatewaySharedSecret: process.env.GATEWAY_SHARED_SECRET!,\n  enforceGatewayOnly: process.env.NODE_ENV === 'production',\n\n  // Outbound subscription client (omit if you don't push subscriptions):\n  internalApiBaseUrl: process.env.IAM_INTERNAL_API_BASE_URL!, // e.g. http://iam-server:10732\n  internalApiKey: process.env.INTERNAL_API_KEY!,              // X-Internal-Api-Key\n  productSlug: 'hangeon-chat',                                // default; overridable per call\n});\n```\n\nInject it where your billing events land. `userId` is the **IAM user UUID** —\nyour backend owns the mapping from its own customer id.\n\n```ts\nimport { Injectable } from '@nestjs/common';\nimport { IamSubscriptionService } from '@drvalue-oss/iam-nestjs';\n\n@Injectable()\nexport class BillingWebhookService {\n  constructor(private readonly subscriptions: IamSubscriptionService) {}\n\n  // Register or update — idempotent on (userId, productSlug). The first call\n  // registers; later calls update in place. Use for every paid-state change.\n  async onPaid(userId: string, nextBillingAt: Date) {\n    await this.subscriptions.upsert(userId, {\n      tier: 'Pro',\n      priceKrw: 12000,\n      status: 'ACTIVE',\n      nextBillingAt,\n      manageUrl: 'https://pay.example.com/manage',\n    });\n  }\n\n  // \"FREE 이용 중\" card — push a free mirror (e.g. on signup) so the user always\n  // has a card. Forces priceKrw 0 / status ACTIVE / metadata.is_free_tier = true.\n  async onSignup(userId: string) {\n    await this.subscriptions.registerFree(userId, {\n      manageUrl: 'https://pay.example.com/pricing', // \"업그레이드 알아보기\" link\n    });\n  }\n\n  // Cancel — renewal stops; tier functional until graceUntil. Thin wrapper that\n  // forces status CANCELED (the server keeps no read endpoint, so pass full state).\n  async onCanceled(userId: string, graceUntil: Date) {\n    await this.subscriptions.cancel(userId, {\n      tier: 'Pro',\n      priceKrw: 12000,\n      graceUntil,\n      manageUrl: 'https://pay.example.com/manage',\n    });\n  }\n\n  // Remove the mirror entirely (e.g. GDPR erasure). For routine downgrades push\n  // status: 'EXPIRED' via upsert instead — that keeps the row for support queries.\n  async onErased(userId: string) {\n    await this.subscriptions.delete(userId);\n  }\n}\n```\n\n**Statuses** mirror IAM's narrow enum: `ACTIVE` / `CANCELED` / `EXPIRED`. There is\nno `FREE` status — a free plan is the `metadata.is_free_tier: true` flag (set for\nyou by `registerFree`), which IAM renders as a \"FREE 이용 중\" card.\n\n**Date fields** (`currentPeriodStart`, `currentPeriodEnd`, `graceUntil`,\n`nextBillingAt`) accept a `Date` or a string. IAM stores them as Java\n`LocalDateTime` (**zone-naive**). A `Date` is serialized to a bare UTC wall-clock\nat seconds precision (`2026-07-01T00:00:00`, no offset), which the server always\naccepts. A `string` is passed through verbatim — keep it **offset-free**: the\nserver tolerates a trailing `Z` (silently dropping it) but **rejects a numeric\noffset** like `+09:00` with a `400`. An invalid `Date` (`NaN`) throws a clear\nerror before the request is sent.\n\n**Errors:** a non-2xx response throws `IamInternalApiError` (`.status`, `.body`,\n`.method`, `.url`); network failures propagate as the raw `fetch` rejection. The\n`productSlug` and `userId` are URL-encoded; an unknown `userId` returns `400`.\nConfig is validated lazily — the first call throws a clear error if\n`internalApiBaseUrl` / `internalApiKey` / `productSlug` are missing.\n\n> Uses the global `fetch` (Node ≥20.19) — no extra runtime dependency. Your\n> service must have network reach to the IAM internal API and hold the\n> `INTERNAL_API_KEY`; treat it as a secret (it bypasses the gateway).\n\n## Looking up users (outbound)\n\nThe module also ships `IamUserService`, an outbound client for IAM's user-lookup\ninternal API. The main reason it exists: the **phone number** is intentionally\nabsent from the JWT and the gateway `X-User-*` headers (per IAM's V19 hardening),\nso a backend that needs it fetches it **server-to-server** here instead of\ntrusting a forwarded header.\n\nIt uses the same `internalApiBaseUrl` + `internalApiKey` config as the\nsubscription client (no `productSlug` needed):\n\n```ts\nIamModule.forRoot({\n  gatewaySharedSecret: process.env.GATEWAY_SHARED_SECRET!,\n  enforceGatewayOnly: process.env.NODE_ENV === 'production',\n\n  internalApiBaseUrl: process.env.IAM_INTERNAL_API_BASE_URL!, // e.g. http://iam-server:10732\n  internalApiKey: process.env.INTERNAL_API_KEY!,              // X-Internal-Api-Key\n});\n```\n\nInject it and resolve a user by id, or by a single natural key (email / phone /\nusername):\n\n```ts\nimport { Injectable } from '@nestjs/common';\nimport { IamUserService } from '@drvalue-oss/iam-nestjs';\n\n@Injectable()\nexport class NotificationService {\n  constructor(private readonly users: IamUserService) {}\n\n  // By IAM user UUID — e.g. read the phone the gateway never forwards.\n  async sendSms(userId: string, message: string) {\n    const user = await this.users.getById(userId);\n    await this.sms.send(user.phone, message);\n  }\n\n  // By natural key — exactly one of email / phone / username. Validated\n  // client-side first, so passing zero or two keys throws before any request.\n  async findByEmail(email: string) {\n    return this.users.lookup({ email }); // → IamUserDetail\n  }\n}\n```\n\nThe response (`IamUserDetail`) carries `id`, `username`, `name`, `email`,\n`phone`, `phoneVerified`, `role`, `enabled`, `lastLoginAt`, `createdAt`,\n`updatedAt`. Date fields are zone-naive strings; `name` / `lastLoginAt` /\n`updatedAt` may be `null`.\n\n**Errors** match the subscription client: a non-2xx throws `IamInternalApiError`\n(`.status`, `.body`, `.method`, `.url`) — an unknown user is `404`. Config is\nvalidated lazily on first use.\n\n> ⚠️ This response is **PII** (phone, email). Keep it server-side; never relay it\n> verbatim to an untrusted client.\n\n## Security notes\n\n- `IamUserGuard` does NOT verify the JWT. Trust is established by `GatewaySignatureGuard` + a network policy that limits ingress to IAM Gateway only. Without the network policy, an attacker who can reach your service directly can forge `X-User-Id: 1` and impersonate any user — `enforceGatewayOnly: true` is your only line of defense. The guard logs a warning at startup when `enforceGatewayOnly` is false.\n- **`@SkipGatewaySignature()` routes must also be `@Public()`** (or must not rely on `req.user`). Skipping the signature removes the proof of gateway origin, so the `X-User-*` headers on that route are forgeable — combining it with `@Roles()`/`@Authenticated()` and trusting the user is an auth bypass.\n- `PLATFORM_ADMIN` bypasses all `@Roles()` checks. Encode group-scoped role checks in a separate guard against `user.activeGroup.role`.\n- `@Roles()` requires at least one role (an empty call throws at startup). A method-level `@Roles()`/`@Authenticated()` overrides class-level `@Public()`, and a method-level `@Roles()` fully replaces a class-level `@Roles()` (the class is a default, not a floor) — audit controllers that mix these.\n\n## License\n\n[MIT](../../LICENSE)\n","readmeFilename":"README.md"}