@pafi-dev/issuer 0.27.2 → 0.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-BRKEJJFQ.js +17 -0
- package/dist/chunk-BRKEJJFQ.js.map +1 -0
- package/dist/{chunk-U3WMORJG.js → chunk-QLNGNH4A.js} +1 -52
- package/dist/{chunk-U3WMORJG.js.map → chunk-QLNGNH4A.js.map} +1 -1
- package/dist/http/index.js +2 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/nestjs/index.cjs +107 -51
- package/dist/nestjs/index.cjs.map +1 -1
- package/dist/nestjs/index.d.cts +90 -2
- package/dist/nestjs/index.d.ts +90 -2
- package/dist/nestjs/index.js +109 -10
- package/dist/nestjs/index.js.map +1 -1
- package/dist/types-CxVXRHLy.d.cts +64 -0
- package/dist/types-CxVXRHLy.d.ts +64 -0
- package/dist/wallet-auth/index.cjs +48 -0
- package/dist/wallet-auth/index.cjs.map +1 -0
- package/dist/wallet-auth/index.d.cts +29 -0
- package/dist/wallet-auth/index.d.ts +29 -0
- package/dist/wallet-auth/index.js +23 -0
- package/dist/wallet-auth/index.js.map +1 -0
- package/package.json +14 -1
package/dist/nestjs/index.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PafiSdkError,
|
|
3
3
|
SDK_ERROR_HTTP_STATUS_CODE,
|
|
4
|
-
__decorateElement,
|
|
5
|
-
__decoratorStart,
|
|
6
|
-
__runInitializers,
|
|
7
4
|
buildErrorEnvelope,
|
|
8
5
|
payloadFromGenericError,
|
|
9
6
|
payloadFromHttpException,
|
|
10
7
|
payloadFromPafiSdkError
|
|
11
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-QLNGNH4A.js";
|
|
9
|
+
import {
|
|
10
|
+
__decorateClass,
|
|
11
|
+
__decorateParam
|
|
12
|
+
} from "../chunk-BRKEJJFQ.js";
|
|
12
13
|
|
|
13
14
|
// src/nestjs/httpExceptionFilter.ts
|
|
14
15
|
import {
|
|
@@ -18,8 +19,6 @@ import {
|
|
|
18
19
|
Logger
|
|
19
20
|
} from "@nestjs/common";
|
|
20
21
|
import { randomUUID } from "crypto";
|
|
21
|
-
var _PafiHttpExceptionFilter_decorators, _init;
|
|
22
|
-
_PafiHttpExceptionFilter_decorators = [Catch()];
|
|
23
22
|
var PafiHttpExceptionFilter = class {
|
|
24
23
|
logger = new Logger("PafiHttpExceptionFilter");
|
|
25
24
|
opts;
|
|
@@ -115,10 +114,110 @@ var PafiHttpExceptionFilter = class {
|
|
|
115
114
|
};
|
|
116
115
|
}
|
|
117
116
|
};
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
PafiHttpExceptionFilter = __decorateClass([
|
|
118
|
+
Catch()
|
|
119
|
+
], PafiHttpExceptionFilter);
|
|
120
|
+
|
|
121
|
+
// src/nestjs/walletAuthJwks.module.ts
|
|
122
|
+
import {
|
|
123
|
+
Module
|
|
124
|
+
} from "@nestjs/common";
|
|
125
|
+
|
|
126
|
+
// src/nestjs/walletAuthJwks.controller.ts
|
|
127
|
+
import {
|
|
128
|
+
Controller,
|
|
129
|
+
Get,
|
|
130
|
+
HttpCode,
|
|
131
|
+
HttpStatus as HttpStatus2,
|
|
132
|
+
Inject,
|
|
133
|
+
Logger as Logger2
|
|
134
|
+
} from "@nestjs/common";
|
|
135
|
+
|
|
136
|
+
// src/nestjs/walletAuthJwks.tokens.ts
|
|
137
|
+
var WALLET_AUTH_JWKS_KEYS = /* @__PURE__ */ Symbol("WALLET_AUTH_JWKS_KEYS");
|
|
138
|
+
|
|
139
|
+
// src/nestjs/walletAuthJwks.controller.ts
|
|
140
|
+
var WalletAuthJwksController = class {
|
|
141
|
+
logger = new Logger2(WalletAuthJwksController.name);
|
|
142
|
+
jwks;
|
|
143
|
+
constructor(keys) {
|
|
144
|
+
this.jwks = { keys };
|
|
145
|
+
const kids = keys.map((k) => k.kid).join(", ");
|
|
146
|
+
this.logger.log(
|
|
147
|
+
`JWKS endpoint ready \u2014 ${keys.length} key(s) published: ${kids || "(empty)"}`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
getJwks() {
|
|
151
|
+
return this.jwks;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
__decorateClass([
|
|
155
|
+
Get("jwks.json"),
|
|
156
|
+
HttpCode(HttpStatus2.OK)
|
|
157
|
+
], WalletAuthJwksController.prototype, "getJwks", 1);
|
|
158
|
+
WalletAuthJwksController = __decorateClass([
|
|
159
|
+
Controller(".well-known"),
|
|
160
|
+
__decorateParam(0, Inject(WALLET_AUTH_JWKS_KEYS))
|
|
161
|
+
], WalletAuthJwksController);
|
|
162
|
+
|
|
163
|
+
// src/nestjs/walletAuthJwks.module.ts
|
|
164
|
+
var WalletAuthJwksModule = class {
|
|
165
|
+
static forRoot(options) {
|
|
166
|
+
return {
|
|
167
|
+
module: WalletAuthJwksModule,
|
|
168
|
+
controllers: [WalletAuthJwksController],
|
|
169
|
+
providers: [
|
|
170
|
+
{
|
|
171
|
+
provide: WALLET_AUTH_JWKS_KEYS,
|
|
172
|
+
useValue: validateKeys(options.keys)
|
|
173
|
+
}
|
|
174
|
+
]
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
static forRootAsync(options) {
|
|
178
|
+
return {
|
|
179
|
+
module: WalletAuthJwksModule,
|
|
180
|
+
imports: options.imports ?? [],
|
|
181
|
+
controllers: [WalletAuthJwksController],
|
|
182
|
+
providers: [
|
|
183
|
+
{
|
|
184
|
+
provide: WALLET_AUTH_JWKS_KEYS,
|
|
185
|
+
inject: options.inject ?? [],
|
|
186
|
+
useFactory: async (...args) => {
|
|
187
|
+
const opts = await options.useFactory(...args);
|
|
188
|
+
return validateKeys(opts.keys);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
WalletAuthJwksModule = __decorateClass([
|
|
196
|
+
Module({})
|
|
197
|
+
], WalletAuthJwksModule);
|
|
198
|
+
function validateKeys(keys) {
|
|
199
|
+
if (!Array.isArray(keys)) {
|
|
200
|
+
throw new Error("WalletAuthJwksModule: `keys` must be an array");
|
|
201
|
+
}
|
|
202
|
+
for (const k of keys) {
|
|
203
|
+
if (!k || typeof k !== "object") {
|
|
204
|
+
throw new Error("WalletAuthJwksModule: each key must be an object");
|
|
205
|
+
}
|
|
206
|
+
if (!k.kty) {
|
|
207
|
+
throw new Error("WalletAuthJwksModule: each key must have a `kty` field");
|
|
208
|
+
}
|
|
209
|
+
if (!k.kid) {
|
|
210
|
+
throw new Error(
|
|
211
|
+
"WalletAuthJwksModule: each key must have a `kid` field \u2014 PAFI gateway uses kid to look up the verification key"
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return keys;
|
|
216
|
+
}
|
|
121
217
|
export {
|
|
122
|
-
PafiHttpExceptionFilter
|
|
218
|
+
PafiHttpExceptionFilter,
|
|
219
|
+
WALLET_AUTH_JWKS_KEYS,
|
|
220
|
+
WalletAuthJwksController,
|
|
221
|
+
WalletAuthJwksModule
|
|
123
222
|
};
|
|
124
223
|
//# sourceMappingURL=index.js.map
|
package/dist/nestjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/nestjs/httpExceptionFilter.ts"],"sourcesContent":["/**\n * NestJS global exception filter that emits the PAFI Stripe-style\n * envelope (see `@pafi-dev/issuer/http`).\n *\n * Wire it once in your bootstrap:\n *\n * ```ts\n * import { PafiHttpExceptionFilter } from \"@pafi-dev/issuer/nestjs\";\n *\n * app.useGlobalFilters(new PafiHttpExceptionFilter());\n * ```\n *\n * Catches every thrown value:\n * - `PafiSdkError` (and subclasses) → typed envelope from `httpStatus`/\n * `code`/`type`/`safeToRetry`/`param`/`metadata`.\n * - `HttpException` (NestJS) → respected status; SDK-shaped body is\n * passed through, plain bodies + ValidationPipe arrays normalized.\n * - any other `Error` → `server_error` 500 with sanitized message.\n *\n * `@nestjs/common` is an optional peer dependency — only required when\n * importing this sub-export.\n */\nimport {\n ArgumentsHost,\n Catch,\n ExceptionFilter,\n HttpException,\n HttpStatus,\n Logger,\n} from \"@nestjs/common\";\nimport { randomUUID } from \"node:crypto\";\n\nimport { PafiSdkError, SDK_ERROR_HTTP_STATUS_CODE } from \"../errors\";\nimport {\n buildErrorEnvelope,\n payloadFromGenericError,\n payloadFromHttpException,\n payloadFromPafiSdkError,\n type NormalizeContext,\n type PafiErrorEnvelope,\n type PafiErrorPayload,\n} from \"../http/errorEnvelope\";\n\ninterface MinimalHttpRequest {\n url?: string;\n headers?: Record<string, string | string[] | undefined>;\n}\ninterface MinimalHttpResponse {\n status(code: number): MinimalHttpResponse;\n json(body: unknown): unknown;\n}\n\n/** Customization knobs. None required — sensible defaults baked in. */\nexport interface PafiHttpExceptionFilterOptions {\n /**\n * Header to read for the request id, lowercase. Default `\"x-request-id\"`.\n * If absent, a UUIDv4 is generated per request.\n */\n requestIdHeader?: string;\n /**\n * Override `Date` for deterministic tests.\n */\n now?: () => Date;\n /**\n * Hook to record/audit normalized errors before sending. Useful for\n * Sentry / Datadog / Pino. Throws are swallowed so a logging bug\n * never masks the real error response.\n */\n onError?: (envelope: PafiErrorEnvelope, raw: unknown) => void;\n}\n\n@Catch()\nexport class PafiHttpExceptionFilter implements ExceptionFilter {\n private readonly logger = new Logger(\"PafiHttpExceptionFilter\");\n private readonly opts: Required<\n Pick<PafiHttpExceptionFilterOptions, \"requestIdHeader\">\n > &\n PafiHttpExceptionFilterOptions;\n\n constructor(options: PafiHttpExceptionFilterOptions = {}) {\n this.opts = {\n requestIdHeader: options.requestIdHeader ?? \"x-request-id\",\n ...options,\n };\n }\n\n catch(exception: unknown, host: ArgumentsHost): void {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse<MinimalHttpResponse>();\n const request = ctx.getRequest<MinimalHttpRequest>();\n\n const headerValue = request?.headers?.[this.opts.requestIdHeader];\n const requestId =\n (Array.isArray(headerValue) ? headerValue[0] : headerValue) ??\n randomUUID();\n\n const normCtx: NormalizeContext = {\n path: request?.url ?? \"\",\n requestId,\n ...(this.opts.now ? { now: this.opts.now } : {}),\n };\n\n const { status, payload } = this.normalize(exception);\n const envelope = buildErrorEnvelope({ status, payload, ctx: normCtx });\n\n if (this.opts.onError) {\n try {\n this.opts.onError(envelope, exception);\n } catch (hookErr) {\n this.logger.warn(\n { err: hookErr instanceof Error ? hookErr.message : String(hookErr) },\n \"onError hook threw; continuing\",\n );\n }\n }\n\n if (status >= 500) {\n this.logger.error(\n {\n err:\n exception instanceof Error\n ? exception.message\n : String(exception),\n stack: exception instanceof Error ? exception.stack : undefined,\n name: exception instanceof Error ? exception.name : undefined,\n path: normCtx.path,\n requestId,\n },\n \"Unhandled error in PafiHttpExceptionFilter\",\n );\n }\n\n response.status(status).json(envelope);\n }\n\n private normalize(exception: unknown): {\n status: number;\n payload: PafiErrorPayload;\n } {\n if (exception instanceof PafiSdkError) {\n return {\n status: SDK_ERROR_HTTP_STATUS_CODE[exception.httpStatus],\n payload: payloadFromPafiSdkError(exception),\n };\n }\n\n if (exception instanceof HttpException) {\n return {\n status: exception.getStatus(),\n payload: payloadFromHttpException({\n statusCode: exception.getStatus(),\n responseBody: exception.getResponse(),\n exceptionName: exception.name,\n fallbackMessage: exception.message,\n }),\n };\n }\n\n if (exception instanceof Error) {\n // Duck-typing for HttpException instances that fail `instanceof`\n // when multiple `@nestjs/common` copies exist in the dep tree.\n const maybeStatus = (exception as { status?: unknown }).status;\n const maybeGetResponse = (\n exception as { getResponse?: () => unknown }\n ).getResponse;\n if (\n typeof maybeStatus === \"number\" &&\n typeof maybeGetResponse === \"function\"\n ) {\n return {\n status: maybeStatus,\n payload: payloadFromHttpException({\n statusCode: maybeStatus,\n responseBody: maybeGetResponse.call(exception),\n exceptionName: exception.name,\n fallbackMessage: exception.message,\n }),\n };\n }\n const maybeStatusOnly =\n typeof maybeStatus === \"number\"\n ? maybeStatus\n : HttpStatus.INTERNAL_SERVER_ERROR;\n return {\n status: maybeStatusOnly,\n payload: payloadFromGenericError(exception),\n };\n }\n\n return {\n status: HttpStatus.INTERNAL_SERVER_ERROR,\n payload: {\n type: \"server_error\",\n code: \"INTERNAL_SERVER_ERROR\",\n message: \"An unexpected error occurred\",\n safeToRetry: false,\n },\n };\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/nestjs/httpExceptionFilter.ts","../../src/nestjs/walletAuthJwks.module.ts","../../src/nestjs/walletAuthJwks.controller.ts","../../src/nestjs/walletAuthJwks.tokens.ts"],"sourcesContent":["/**\n * NestJS global exception filter that emits the PAFI Stripe-style\n * envelope (see `@pafi-dev/issuer/http`).\n *\n * Wire it once in your bootstrap:\n *\n * ```ts\n * import { PafiHttpExceptionFilter } from \"@pafi-dev/issuer/nestjs\";\n *\n * app.useGlobalFilters(new PafiHttpExceptionFilter());\n * ```\n *\n * Catches every thrown value:\n * - `PafiSdkError` (and subclasses) → typed envelope from `httpStatus`/\n * `code`/`type`/`safeToRetry`/`param`/`metadata`.\n * - `HttpException` (NestJS) → respected status; SDK-shaped body is\n * passed through, plain bodies + ValidationPipe arrays normalized.\n * - any other `Error` → `server_error` 500 with sanitized message.\n *\n * `@nestjs/common` is an optional peer dependency — only required when\n * importing this sub-export.\n */\nimport {\n ArgumentsHost,\n Catch,\n ExceptionFilter,\n HttpException,\n HttpStatus,\n Logger,\n} from \"@nestjs/common\";\nimport { randomUUID } from \"node:crypto\";\n\nimport { PafiSdkError, SDK_ERROR_HTTP_STATUS_CODE } from \"../errors\";\nimport {\n buildErrorEnvelope,\n payloadFromGenericError,\n payloadFromHttpException,\n payloadFromPafiSdkError,\n type NormalizeContext,\n type PafiErrorEnvelope,\n type PafiErrorPayload,\n} from \"../http/errorEnvelope\";\n\ninterface MinimalHttpRequest {\n url?: string;\n headers?: Record<string, string | string[] | undefined>;\n}\ninterface MinimalHttpResponse {\n status(code: number): MinimalHttpResponse;\n json(body: unknown): unknown;\n}\n\n/** Customization knobs. None required — sensible defaults baked in. */\nexport interface PafiHttpExceptionFilterOptions {\n /**\n * Header to read for the request id, lowercase. Default `\"x-request-id\"`.\n * If absent, a UUIDv4 is generated per request.\n */\n requestIdHeader?: string;\n /**\n * Override `Date` for deterministic tests.\n */\n now?: () => Date;\n /**\n * Hook to record/audit normalized errors before sending. Useful for\n * Sentry / Datadog / Pino. Throws are swallowed so a logging bug\n * never masks the real error response.\n */\n onError?: (envelope: PafiErrorEnvelope, raw: unknown) => void;\n}\n\n@Catch()\nexport class PafiHttpExceptionFilter implements ExceptionFilter {\n private readonly logger = new Logger(\"PafiHttpExceptionFilter\");\n private readonly opts: Required<\n Pick<PafiHttpExceptionFilterOptions, \"requestIdHeader\">\n > &\n PafiHttpExceptionFilterOptions;\n\n constructor(options: PafiHttpExceptionFilterOptions = {}) {\n this.opts = {\n requestIdHeader: options.requestIdHeader ?? \"x-request-id\",\n ...options,\n };\n }\n\n catch(exception: unknown, host: ArgumentsHost): void {\n const ctx = host.switchToHttp();\n const response = ctx.getResponse<MinimalHttpResponse>();\n const request = ctx.getRequest<MinimalHttpRequest>();\n\n const headerValue = request?.headers?.[this.opts.requestIdHeader];\n const requestId =\n (Array.isArray(headerValue) ? headerValue[0] : headerValue) ??\n randomUUID();\n\n const normCtx: NormalizeContext = {\n path: request?.url ?? \"\",\n requestId,\n ...(this.opts.now ? { now: this.opts.now } : {}),\n };\n\n const { status, payload } = this.normalize(exception);\n const envelope = buildErrorEnvelope({ status, payload, ctx: normCtx });\n\n if (this.opts.onError) {\n try {\n this.opts.onError(envelope, exception);\n } catch (hookErr) {\n this.logger.warn(\n { err: hookErr instanceof Error ? hookErr.message : String(hookErr) },\n \"onError hook threw; continuing\",\n );\n }\n }\n\n if (status >= 500) {\n this.logger.error(\n {\n err:\n exception instanceof Error\n ? exception.message\n : String(exception),\n stack: exception instanceof Error ? exception.stack : undefined,\n name: exception instanceof Error ? exception.name : undefined,\n path: normCtx.path,\n requestId,\n },\n \"Unhandled error in PafiHttpExceptionFilter\",\n );\n }\n\n response.status(status).json(envelope);\n }\n\n private normalize(exception: unknown): {\n status: number;\n payload: PafiErrorPayload;\n } {\n if (exception instanceof PafiSdkError) {\n return {\n status: SDK_ERROR_HTTP_STATUS_CODE[exception.httpStatus],\n payload: payloadFromPafiSdkError(exception),\n };\n }\n\n if (exception instanceof HttpException) {\n return {\n status: exception.getStatus(),\n payload: payloadFromHttpException({\n statusCode: exception.getStatus(),\n responseBody: exception.getResponse(),\n exceptionName: exception.name,\n fallbackMessage: exception.message,\n }),\n };\n }\n\n if (exception instanceof Error) {\n // Duck-typing for HttpException instances that fail `instanceof`\n // when multiple `@nestjs/common` copies exist in the dep tree.\n const maybeStatus = (exception as { status?: unknown }).status;\n const maybeGetResponse = (\n exception as { getResponse?: () => unknown }\n ).getResponse;\n if (\n typeof maybeStatus === \"number\" &&\n typeof maybeGetResponse === \"function\"\n ) {\n return {\n status: maybeStatus,\n payload: payloadFromHttpException({\n statusCode: maybeStatus,\n responseBody: maybeGetResponse.call(exception),\n exceptionName: exception.name,\n fallbackMessage: exception.message,\n }),\n };\n }\n const maybeStatusOnly =\n typeof maybeStatus === \"number\"\n ? maybeStatus\n : HttpStatus.INTERNAL_SERVER_ERROR;\n return {\n status: maybeStatusOnly,\n payload: payloadFromGenericError(exception),\n };\n }\n\n return {\n status: HttpStatus.INTERNAL_SERVER_ERROR,\n payload: {\n type: \"server_error\",\n code: \"INTERNAL_SERVER_ERROR\",\n message: \"An unexpected error occurred\",\n safeToRetry: false,\n },\n };\n }\n}\n","import {\n Module,\n type DynamicModule,\n type FactoryProvider,\n} from \"@nestjs/common\";\nimport type { IssuerPublicJwk } from \"../wallet-auth/types\";\nimport { WalletAuthJwksController } from \"./walletAuthJwks.controller\";\nimport { WALLET_AUTH_JWKS_KEYS } from \"./walletAuthJwks.tokens\";\n\nexport interface WalletAuthJwksOptions {\n /**\n * One or more public JWKs to publish. During key rotation, include\n * BOTH the active and previous keys so JWTs signed with the older\n * kid can still be verified by the PAFI gateway until the rotation\n * window closes.\n */\n keys: IssuerPublicJwk[];\n}\n\nexport interface WalletAuthJwksAsyncOptions {\n /** Modules to import for the factory's DI. */\n imports?: NonNullable<DynamicModule[\"imports\"]>;\n /** Providers to inject into useFactory — same shape as FactoryProvider.inject. */\n inject?: FactoryProvider[\"inject\"];\n /** Builds options at runtime — read env, decrypt secrets, etc. */\n useFactory: (...args: any[]) => Promise<WalletAuthJwksOptions> | WalletAuthJwksOptions;\n}\n\n/**\n * Drop-in NestJS module that publishes the issuer's public signing\n * key set at GET /.well-known/jwks.json.\n *\n * Every issuer backend integrating with the PAFI Wallet Auth Gateway\n * needs this endpoint so the gateway can fetch their public key and\n * verify issuer JWT signatures.\n *\n * @example Sync registration\n * import { WalletAuthJwksModule } from '@pafi-dev/issuer/nestjs';\n *\n * @Module({\n * imports: [\n * WalletAuthJwksModule.forRoot({\n * keys: [JSON.parse(process.env.ISSUER_PUBLIC_JWK_JSON!)],\n * }),\n * ],\n * })\n * export class AppModule {}\n *\n * @example Async registration (read from ConfigService)\n * import { ConfigModule, ConfigService } from '@nestjs/config';\n * import { WalletAuthJwksModule } from '@pafi-dev/issuer/nestjs';\n *\n * @Module({\n * imports: [\n * WalletAuthJwksModule.forRootAsync({\n * imports: [ConfigModule],\n * inject: [ConfigService],\n * useFactory: (config: ConfigService) => ({\n * keys: [JSON.parse(config.getOrThrow('ISSUER_PUBLIC_JWK_JSON'))],\n * }),\n * }),\n * ],\n * })\n * export class AppModule {}\n *\n * @example Rotation window — publish 2 keys simultaneously\n * keys: [\n * JSON.parse(process.env.ISSUER_PUBLIC_JWK_JSON_ACTIVE!),\n * JSON.parse(process.env.ISSUER_PUBLIC_JWK_JSON_PREVIOUS!),\n * ]\n */\n@Module({})\nexport class WalletAuthJwksModule {\n static forRoot(options: WalletAuthJwksOptions): DynamicModule {\n return {\n module: WalletAuthJwksModule,\n controllers: [WalletAuthJwksController],\n providers: [\n {\n provide: WALLET_AUTH_JWKS_KEYS,\n useValue: validateKeys(options.keys),\n },\n ],\n };\n }\n\n static forRootAsync(options: WalletAuthJwksAsyncOptions): DynamicModule {\n return {\n module: WalletAuthJwksModule,\n imports: options.imports ?? [],\n controllers: [WalletAuthJwksController],\n providers: [\n {\n provide: WALLET_AUTH_JWKS_KEYS,\n inject: options.inject ?? [],\n useFactory: async (...args: unknown[]) => {\n const opts = await options.useFactory(...args);\n return validateKeys(opts.keys);\n },\n },\n ],\n };\n }\n}\n\n/**\n * Reject misconfiguration loudly at boot rather than serving an\n * invalid JWKS. Missing `kid` would mean the gateway can't match\n * the key against inbound JWT headers.\n */\nfunction validateKeys(keys: IssuerPublicJwk[]): IssuerPublicJwk[] {\n if (!Array.isArray(keys)) {\n throw new Error(\"WalletAuthJwksModule: `keys` must be an array\");\n }\n for (const k of keys) {\n if (!k || typeof k !== \"object\") {\n throw new Error(\"WalletAuthJwksModule: each key must be an object\");\n }\n if (!k.kty) {\n throw new Error(\"WalletAuthJwksModule: each key must have a `kty` field\");\n }\n if (!k.kid) {\n throw new Error(\n \"WalletAuthJwksModule: each key must have a `kid` field — PAFI gateway uses kid to look up the verification key\",\n );\n }\n }\n return keys;\n}\n","import {\n Controller,\n Get,\n HttpCode,\n HttpStatus,\n Inject,\n Logger,\n} from \"@nestjs/common\";\nimport type { IssuerPublicJwk } from \"../wallet-auth/types\";\nimport { WALLET_AUTH_JWKS_KEYS } from \"./walletAuthJwks.tokens\";\n\n/**\n * Publishes the issuer's public signing key set as RFC 7517 JWKS.\n *\n * The PAFI Wallet Auth Gateway fetches this URL to verify signatures\n * on issuer JWTs that this backend mints (the JWT travelling in\n * /v1/token-exchange's `issuer_jwt` body field).\n *\n * Mounted at GET /.well-known/jwks.json — no auth, public by design.\n */\n@Controller(\".well-known\")\nexport class WalletAuthJwksController {\n private readonly logger = new Logger(WalletAuthJwksController.name);\n private readonly jwks: { keys: IssuerPublicJwk[] };\n\n constructor(\n @Inject(WALLET_AUTH_JWKS_KEYS)\n keys: IssuerPublicJwk[],\n ) {\n this.jwks = { keys };\n const kids = keys.map((k) => k.kid).join(\", \");\n this.logger.log(\n `JWKS endpoint ready — ${keys.length} key(s) published: ${kids || \"(empty)\"}`,\n );\n }\n\n @Get(\"jwks.json\")\n @HttpCode(HttpStatus.OK)\n getJwks(): { keys: IssuerPublicJwk[] } {\n return this.jwks;\n }\n}\n","/** DI token: array of public JWKs to publish at /.well-known/jwks.json. */\nexport const WALLET_AUTH_JWKS_KEYS = Symbol(\"WALLET_AUTH_JWKS_KEYS\");\n"],"mappings":"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","names":["HttpStatus","Logger","Logger","HttpStatus"]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { JWK } from 'jose';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Public JWK shape — what an issuer publishes at /.well-known/jwks.json
|
|
5
|
+
* for the PAFI Wallet Auth Gateway to fetch and verify signatures.
|
|
6
|
+
*
|
|
7
|
+
* Must include `kid` so the gateway can match it against the `kid`
|
|
8
|
+
* header in inbound issuer JWTs (multiple keys can be published
|
|
9
|
+
* simultaneously during a rotation window).
|
|
10
|
+
*/
|
|
11
|
+
interface IssuerPublicJwk extends JWK {
|
|
12
|
+
kty: string;
|
|
13
|
+
kid: string;
|
|
14
|
+
alg?: string;
|
|
15
|
+
use?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Private JWK — held by the issuer backend, never published.
|
|
19
|
+
* Used by `signIssuerJwt` to mint JWTs the gateway can verify.
|
|
20
|
+
*/
|
|
21
|
+
interface IssuerPrivateJwk extends JWK {
|
|
22
|
+
kty: string;
|
|
23
|
+
kid: string;
|
|
24
|
+
alg?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Parameters for minting an issuer JWT to send to the PAFI gateway.
|
|
28
|
+
*/
|
|
29
|
+
interface SignIssuerJwtParams {
|
|
30
|
+
/** Private signing key (ES256 / ES384 / RS256 / RS384 / RS512 / EdDSA). */
|
|
31
|
+
privateJwk: IssuerPrivateJwk;
|
|
32
|
+
/** Stable identifier for the user being attested (issuer's internal user id). */
|
|
33
|
+
sub: string;
|
|
34
|
+
/** This issuer backend's identity URL — must match `expected_iss` in PAFI's `issuers` row. */
|
|
35
|
+
iss: string;
|
|
36
|
+
/**
|
|
37
|
+
* Expected audience — PAFI gateway's per-env audience claim
|
|
38
|
+
* (e.g. `pafi-gateway-prod` / `pafi-gateway-staging` / `pafi-gateway-dev`).
|
|
39
|
+
* Provided by PAFI ops at onboarding time.
|
|
40
|
+
*/
|
|
41
|
+
aud: string;
|
|
42
|
+
/**
|
|
43
|
+
* Optional algorithm override. Defaults to private JWK's `alg`, then `ES256`.
|
|
44
|
+
* Must match a value in PAFI gateway's algorithm whitelist.
|
|
45
|
+
*/
|
|
46
|
+
alg?: "ES256" | "ES384" | "RS256" | "RS384" | "RS512" | "EdDSA";
|
|
47
|
+
/**
|
|
48
|
+
* Lifetime in seconds. Default 60 — keep short. PAFI gateway also
|
|
49
|
+
* enforces `max_token_age_sec` (default 60) regardless of `exp`.
|
|
50
|
+
*/
|
|
51
|
+
expiresInSec?: number;
|
|
52
|
+
/**
|
|
53
|
+
* Optional unique JWT id. Defaults to a random UUID. PAFI gateway
|
|
54
|
+
* uses this for replay protection — must be unique per request.
|
|
55
|
+
*/
|
|
56
|
+
jti?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Optional additional claims merged into the payload. Use sparingly —
|
|
59
|
+
* PAFI gateway only reads `iss`, `sub`, `aud`, `iat`, `exp`, `jti`.
|
|
60
|
+
*/
|
|
61
|
+
extra?: Record<string, unknown>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type { IssuerPrivateJwk as I, SignIssuerJwtParams as S, IssuerPublicJwk as a };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { JWK } from 'jose';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Public JWK shape — what an issuer publishes at /.well-known/jwks.json
|
|
5
|
+
* for the PAFI Wallet Auth Gateway to fetch and verify signatures.
|
|
6
|
+
*
|
|
7
|
+
* Must include `kid` so the gateway can match it against the `kid`
|
|
8
|
+
* header in inbound issuer JWTs (multiple keys can be published
|
|
9
|
+
* simultaneously during a rotation window).
|
|
10
|
+
*/
|
|
11
|
+
interface IssuerPublicJwk extends JWK {
|
|
12
|
+
kty: string;
|
|
13
|
+
kid: string;
|
|
14
|
+
alg?: string;
|
|
15
|
+
use?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Private JWK — held by the issuer backend, never published.
|
|
19
|
+
* Used by `signIssuerJwt` to mint JWTs the gateway can verify.
|
|
20
|
+
*/
|
|
21
|
+
interface IssuerPrivateJwk extends JWK {
|
|
22
|
+
kty: string;
|
|
23
|
+
kid: string;
|
|
24
|
+
alg?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Parameters for minting an issuer JWT to send to the PAFI gateway.
|
|
28
|
+
*/
|
|
29
|
+
interface SignIssuerJwtParams {
|
|
30
|
+
/** Private signing key (ES256 / ES384 / RS256 / RS384 / RS512 / EdDSA). */
|
|
31
|
+
privateJwk: IssuerPrivateJwk;
|
|
32
|
+
/** Stable identifier for the user being attested (issuer's internal user id). */
|
|
33
|
+
sub: string;
|
|
34
|
+
/** This issuer backend's identity URL — must match `expected_iss` in PAFI's `issuers` row. */
|
|
35
|
+
iss: string;
|
|
36
|
+
/**
|
|
37
|
+
* Expected audience — PAFI gateway's per-env audience claim
|
|
38
|
+
* (e.g. `pafi-gateway-prod` / `pafi-gateway-staging` / `pafi-gateway-dev`).
|
|
39
|
+
* Provided by PAFI ops at onboarding time.
|
|
40
|
+
*/
|
|
41
|
+
aud: string;
|
|
42
|
+
/**
|
|
43
|
+
* Optional algorithm override. Defaults to private JWK's `alg`, then `ES256`.
|
|
44
|
+
* Must match a value in PAFI gateway's algorithm whitelist.
|
|
45
|
+
*/
|
|
46
|
+
alg?: "ES256" | "ES384" | "RS256" | "RS384" | "RS512" | "EdDSA";
|
|
47
|
+
/**
|
|
48
|
+
* Lifetime in seconds. Default 60 — keep short. PAFI gateway also
|
|
49
|
+
* enforces `max_token_age_sec` (default 60) regardless of `exp`.
|
|
50
|
+
*/
|
|
51
|
+
expiresInSec?: number;
|
|
52
|
+
/**
|
|
53
|
+
* Optional unique JWT id. Defaults to a random UUID. PAFI gateway
|
|
54
|
+
* uses this for replay protection — must be unique per request.
|
|
55
|
+
*/
|
|
56
|
+
jti?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Optional additional claims merged into the payload. Use sparingly —
|
|
59
|
+
* PAFI gateway only reads `iss`, `sub`, `aud`, `iat`, `exp`, `jti`.
|
|
60
|
+
*/
|
|
61
|
+
extra?: Record<string, unknown>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type { IssuerPrivateJwk as I, SignIssuerJwtParams as S, IssuerPublicJwk as a };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/wallet-auth/index.ts
|
|
21
|
+
var wallet_auth_exports = {};
|
|
22
|
+
__export(wallet_auth_exports, {
|
|
23
|
+
signIssuerJwt: () => signIssuerJwt
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(wallet_auth_exports);
|
|
26
|
+
|
|
27
|
+
// src/wallet-auth/signIssuerJwt.ts
|
|
28
|
+
var import_jose = require("jose");
|
|
29
|
+
var import_node_crypto = require("crypto");
|
|
30
|
+
async function signIssuerJwt(params) {
|
|
31
|
+
const alg = params.alg ?? params.privateJwk.alg ?? "ES256";
|
|
32
|
+
const kid = params.privateJwk.kid;
|
|
33
|
+
if (!kid) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
"signIssuerJwt: privateJwk.kid is required (gateway uses kid to look up the verification key)"
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
const key = await (0, import_jose.importJWK)(params.privateJwk, alg);
|
|
39
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
40
|
+
const lifetime = params.expiresInSec ?? 60;
|
|
41
|
+
const builder = new import_jose.SignJWT({ ...params.extra ?? {} }).setProtectedHeader({ alg, typ: "JWT", kid }).setIssuer(params.iss).setSubject(params.sub).setAudience(params.aud).setIssuedAt(now).setExpirationTime(now + lifetime).setJti(params.jti ?? (0, import_node_crypto.randomUUID)());
|
|
42
|
+
return builder.sign(key);
|
|
43
|
+
}
|
|
44
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
45
|
+
0 && (module.exports = {
|
|
46
|
+
signIssuerJwt
|
|
47
|
+
});
|
|
48
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/wallet-auth/index.ts","../../src/wallet-auth/signIssuerJwt.ts"],"sourcesContent":["/**\n * Wallet-auth helpers — for issuer backends that integrate with the\n * PAFI Wallet Auth Gateway.\n *\n * Sub-modules:\n * - signIssuerJwt : mint an issuer JWT to send to the gateway\n * - types : IssuerPublicJwk, IssuerPrivateJwk, SignIssuerJwtParams\n *\n * NestJS-specific helpers (drop-in JWKS publishing controller) live\n * under `@pafi-dev/issuer/nestjs`.\n */\n\nexport { signIssuerJwt } from \"./signIssuerJwt\";\nexport type {\n IssuerPublicJwk,\n IssuerPrivateJwk,\n SignIssuerJwtParams,\n} from \"./types\";\n","import { importJWK, SignJWT } from \"jose\";\nimport { randomUUID } from \"node:crypto\";\nimport type { SignIssuerJwtParams } from \"./types\";\n\n/**\n * Mint an issuer JWT for the PAFI Wallet Auth Gateway.\n *\n * Returns a compact JWT (header.payload.signature) ready to send as\n * the `issuer_jwt` body field in `POST /v1/token-exchange`.\n *\n * The matching public key must be reachable at the issuer's\n * `/.well-known/jwks.json` (see `WalletAuthJwksModule`) so the\n * gateway can verify the signature.\n *\n * @example\n * import { signIssuerJwt } from '@pafi-dev/issuer/wallet-auth';\n *\n * const jwt = await signIssuerJwt({\n * privateJwk: JSON.parse(process.env.ISSUER_PRIVATE_JWK_JSON!),\n * iss: 'https://gg56.com',\n * sub: 'gg56_user_99',\n * aud: 'pafi-gateway-prod',\n * });\n *\n * // POST { issuer_id: 'GG56', issuer_jwt: jwt } to gateway\n */\nexport async function signIssuerJwt(\n params: SignIssuerJwtParams,\n): Promise<string> {\n const alg = params.alg ?? params.privateJwk.alg ?? \"ES256\";\n const kid = params.privateJwk.kid;\n\n if (!kid) {\n throw new Error(\n \"signIssuerJwt: privateJwk.kid is required (gateway uses kid to look up the verification key)\",\n );\n }\n\n const key = await importJWK(params.privateJwk, alg);\n const now = Math.floor(Date.now() / 1000);\n const lifetime = params.expiresInSec ?? 60;\n\n const builder = new SignJWT({ ...(params.extra ?? {}) })\n .setProtectedHeader({ alg, typ: \"JWT\", kid })\n .setIssuer(params.iss)\n .setSubject(params.sub)\n .setAudience(params.aud)\n .setIssuedAt(now)\n .setExpirationTime(now + lifetime)\n .setJti(params.jti ?? randomUUID());\n\n return builder.sign(key);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAAmC;AACnC,yBAA2B;AAyB3B,eAAsB,cACpB,QACiB;AACjB,QAAM,MAAM,OAAO,OAAO,OAAO,WAAW,OAAO;AACnD,QAAM,MAAM,OAAO,WAAW;AAE9B,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,MAAM,UAAM,uBAAU,OAAO,YAAY,GAAG;AAClD,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,WAAW,OAAO,gBAAgB;AAExC,QAAM,UAAU,IAAI,oBAAQ,EAAE,GAAI,OAAO,SAAS,CAAC,EAAG,CAAC,EACpD,mBAAmB,EAAE,KAAK,KAAK,OAAO,IAAI,CAAC,EAC3C,UAAU,OAAO,GAAG,EACpB,WAAW,OAAO,GAAG,EACrB,YAAY,OAAO,GAAG,EACtB,YAAY,GAAG,EACf,kBAAkB,MAAM,QAAQ,EAChC,OAAO,OAAO,WAAO,+BAAW,CAAC;AAEpC,SAAO,QAAQ,KAAK,GAAG;AACzB;","names":[]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { S as SignIssuerJwtParams } from '../types-CxVXRHLy.cjs';
|
|
2
|
+
export { I as IssuerPrivateJwk, a as IssuerPublicJwk } from '../types-CxVXRHLy.cjs';
|
|
3
|
+
import 'jose';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Mint an issuer JWT for the PAFI Wallet Auth Gateway.
|
|
7
|
+
*
|
|
8
|
+
* Returns a compact JWT (header.payload.signature) ready to send as
|
|
9
|
+
* the `issuer_jwt` body field in `POST /v1/token-exchange`.
|
|
10
|
+
*
|
|
11
|
+
* The matching public key must be reachable at the issuer's
|
|
12
|
+
* `/.well-known/jwks.json` (see `WalletAuthJwksModule`) so the
|
|
13
|
+
* gateway can verify the signature.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* import { signIssuerJwt } from '@pafi-dev/issuer/wallet-auth';
|
|
17
|
+
*
|
|
18
|
+
* const jwt = await signIssuerJwt({
|
|
19
|
+
* privateJwk: JSON.parse(process.env.ISSUER_PRIVATE_JWK_JSON!),
|
|
20
|
+
* iss: 'https://gg56.com',
|
|
21
|
+
* sub: 'gg56_user_99',
|
|
22
|
+
* aud: 'pafi-gateway-prod',
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* // POST { issuer_id: 'GG56', issuer_jwt: jwt } to gateway
|
|
26
|
+
*/
|
|
27
|
+
declare function signIssuerJwt(params: SignIssuerJwtParams): Promise<string>;
|
|
28
|
+
|
|
29
|
+
export { SignIssuerJwtParams, signIssuerJwt };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { S as SignIssuerJwtParams } from '../types-CxVXRHLy.js';
|
|
2
|
+
export { I as IssuerPrivateJwk, a as IssuerPublicJwk } from '../types-CxVXRHLy.js';
|
|
3
|
+
import 'jose';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Mint an issuer JWT for the PAFI Wallet Auth Gateway.
|
|
7
|
+
*
|
|
8
|
+
* Returns a compact JWT (header.payload.signature) ready to send as
|
|
9
|
+
* the `issuer_jwt` body field in `POST /v1/token-exchange`.
|
|
10
|
+
*
|
|
11
|
+
* The matching public key must be reachable at the issuer's
|
|
12
|
+
* `/.well-known/jwks.json` (see `WalletAuthJwksModule`) so the
|
|
13
|
+
* gateway can verify the signature.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* import { signIssuerJwt } from '@pafi-dev/issuer/wallet-auth';
|
|
17
|
+
*
|
|
18
|
+
* const jwt = await signIssuerJwt({
|
|
19
|
+
* privateJwk: JSON.parse(process.env.ISSUER_PRIVATE_JWK_JSON!),
|
|
20
|
+
* iss: 'https://gg56.com',
|
|
21
|
+
* sub: 'gg56_user_99',
|
|
22
|
+
* aud: 'pafi-gateway-prod',
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* // POST { issuer_id: 'GG56', issuer_jwt: jwt } to gateway
|
|
26
|
+
*/
|
|
27
|
+
declare function signIssuerJwt(params: SignIssuerJwtParams): Promise<string>;
|
|
28
|
+
|
|
29
|
+
export { SignIssuerJwtParams, signIssuerJwt };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import "../chunk-BRKEJJFQ.js";
|
|
2
|
+
|
|
3
|
+
// src/wallet-auth/signIssuerJwt.ts
|
|
4
|
+
import { importJWK, SignJWT } from "jose";
|
|
5
|
+
import { randomUUID } from "crypto";
|
|
6
|
+
async function signIssuerJwt(params) {
|
|
7
|
+
const alg = params.alg ?? params.privateJwk.alg ?? "ES256";
|
|
8
|
+
const kid = params.privateJwk.kid;
|
|
9
|
+
if (!kid) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
"signIssuerJwt: privateJwk.kid is required (gateway uses kid to look up the verification key)"
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
const key = await importJWK(params.privateJwk, alg);
|
|
15
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
16
|
+
const lifetime = params.expiresInSec ?? 60;
|
|
17
|
+
const builder = new SignJWT({ ...params.extra ?? {} }).setProtectedHeader({ alg, typ: "JWT", kid }).setIssuer(params.iss).setSubject(params.sub).setAudience(params.aud).setIssuedAt(now).setExpirationTime(now + lifetime).setJti(params.jti ?? randomUUID());
|
|
18
|
+
return builder.sign(key);
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
signIssuerJwt
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/wallet-auth/signIssuerJwt.ts"],"sourcesContent":["import { importJWK, SignJWT } from \"jose\";\nimport { randomUUID } from \"node:crypto\";\nimport type { SignIssuerJwtParams } from \"./types\";\n\n/**\n * Mint an issuer JWT for the PAFI Wallet Auth Gateway.\n *\n * Returns a compact JWT (header.payload.signature) ready to send as\n * the `issuer_jwt` body field in `POST /v1/token-exchange`.\n *\n * The matching public key must be reachable at the issuer's\n * `/.well-known/jwks.json` (see `WalletAuthJwksModule`) so the\n * gateway can verify the signature.\n *\n * @example\n * import { signIssuerJwt } from '@pafi-dev/issuer/wallet-auth';\n *\n * const jwt = await signIssuerJwt({\n * privateJwk: JSON.parse(process.env.ISSUER_PRIVATE_JWK_JSON!),\n * iss: 'https://gg56.com',\n * sub: 'gg56_user_99',\n * aud: 'pafi-gateway-prod',\n * });\n *\n * // POST { issuer_id: 'GG56', issuer_jwt: jwt } to gateway\n */\nexport async function signIssuerJwt(\n params: SignIssuerJwtParams,\n): Promise<string> {\n const alg = params.alg ?? params.privateJwk.alg ?? \"ES256\";\n const kid = params.privateJwk.kid;\n\n if (!kid) {\n throw new Error(\n \"signIssuerJwt: privateJwk.kid is required (gateway uses kid to look up the verification key)\",\n );\n }\n\n const key = await importJWK(params.privateJwk, alg);\n const now = Math.floor(Date.now() / 1000);\n const lifetime = params.expiresInSec ?? 60;\n\n const builder = new SignJWT({ ...(params.extra ?? {}) })\n .setProtectedHeader({ alg, typ: \"JWT\", kid })\n .setIssuer(params.iss)\n .setSubject(params.sub)\n .setAudience(params.aud)\n .setIssuedAt(now)\n .setExpirationTime(now + lifetime)\n .setJti(params.jti ?? randomUUID());\n\n return builder.sign(key);\n}\n"],"mappings":";;;AAAA,SAAS,WAAW,eAAe;AACnC,SAAS,kBAAkB;AAyB3B,eAAsB,cACpB,QACiB;AACjB,QAAM,MAAM,OAAO,OAAO,OAAO,WAAW,OAAO;AACnD,QAAM,MAAM,OAAO,WAAW;AAE9B,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,MAAM,MAAM,UAAU,OAAO,YAAY,GAAG;AAClD,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,WAAW,OAAO,gBAAgB;AAExC,QAAM,UAAU,IAAI,QAAQ,EAAE,GAAI,OAAO,SAAS,CAAC,EAAG,CAAC,EACpD,mBAAmB,EAAE,KAAK,KAAK,OAAO,IAAI,CAAC,EAC3C,UAAU,OAAO,GAAG,EACpB,WAAW,OAAO,GAAG,EACrB,YAAY,OAAO,GAAG,EACtB,YAAY,GAAG,EACf,kBAAkB,MAAM,QAAQ,EAChC,OAAO,OAAO,OAAO,WAAW,CAAC;AAEpC,SAAO,QAAQ,KAAK,GAAG;AACzB;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pafi-dev/issuer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.1",
|
|
4
4
|
"description": "Issuer backend API and services for the PAFI point token system",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -45,6 +45,16 @@
|
|
|
45
45
|
"types": "./dist/nestjs/index.d.cts",
|
|
46
46
|
"default": "./dist/nestjs/index.cjs"
|
|
47
47
|
}
|
|
48
|
+
},
|
|
49
|
+
"./wallet-auth": {
|
|
50
|
+
"import": {
|
|
51
|
+
"types": "./dist/wallet-auth/index.d.ts",
|
|
52
|
+
"default": "./dist/wallet-auth/index.js"
|
|
53
|
+
},
|
|
54
|
+
"require": {
|
|
55
|
+
"types": "./dist/wallet-auth/index.d.cts",
|
|
56
|
+
"default": "./dist/wallet-auth/index.cjs"
|
|
57
|
+
}
|
|
48
58
|
}
|
|
49
59
|
},
|
|
50
60
|
"typesVersions": {
|
|
@@ -54,6 +64,9 @@
|
|
|
54
64
|
],
|
|
55
65
|
"nestjs": [
|
|
56
66
|
"./dist/nestjs/index.d.ts"
|
|
67
|
+
],
|
|
68
|
+
"wallet-auth": [
|
|
69
|
+
"./dist/wallet-auth/index.d.ts"
|
|
57
70
|
]
|
|
58
71
|
}
|
|
59
72
|
},
|