@pafi-dev/issuer 0.38.0 → 0.39.0

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.
@@ -0,0 +1,657 @@
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
+ var __decorateClass = (decorators, target, key, kind) => {
20
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
21
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
22
+ if (decorator = decorators[i])
23
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
24
+ if (kind && result) __defProp(target, key, result);
25
+ return result;
26
+ };
27
+ var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
28
+
29
+ // src/direct-auth/index.ts
30
+ var direct_auth_exports = {};
31
+ __export(direct_auth_exports, {
32
+ EmailStartRequestDto: () => EmailStartRequestDto,
33
+ EmailStartResponseDto: () => EmailStartResponseDto,
34
+ EmailVerifyRequestDto: () => EmailVerifyRequestDto,
35
+ GoogleExchangeRequestDto: () => GoogleExchangeRequestDto,
36
+ KakaoExchangeRequestDto: () => KakaoExchangeRequestDto,
37
+ PAFI_DIRECT_AUTH_MODULE_OPTIONS: () => PAFI_DIRECT_AUTH_MODULE_OPTIONS,
38
+ PafiAuthClientProvider: () => PafiAuthClientProvider,
39
+ PafiAuthSuccessDto: () => PafiAuthSuccessDto,
40
+ PafiDirectAuthController: () => PafiDirectAuthController,
41
+ PafiDirectAuthModule: () => PafiDirectAuthModule,
42
+ PafiDirectAuthService: () => PafiDirectAuthService,
43
+ PafiSessionVerifierService: () => PafiSessionVerifierService,
44
+ SESSION_TOKEN_MINTER: () => SESSION_TOKEN_MINTER,
45
+ USER_STORE: () => USER_STORE
46
+ });
47
+ module.exports = __toCommonJS(direct_auth_exports);
48
+
49
+ // src/direct-auth/pafi-direct-auth.module.ts
50
+ var import_common5 = require("@nestjs/common");
51
+
52
+ // src/direct-auth/services/pafi-auth-client.provider.ts
53
+ var import_common = require("@nestjs/common");
54
+
55
+ // src/auth-client/pafi-auth-client.ts
56
+ var import_node_crypto2 = require("crypto");
57
+
58
+ // src/auth-client/sign-client-assertion.ts
59
+ var import_jose = require("jose");
60
+ var import_node_crypto = require("crypto");
61
+ async function signClientAssertion(args) {
62
+ const alg = args.alg ?? args.privateJwk.alg ?? "ES256";
63
+ const key = await (0, import_jose.importJWK)(args.privateJwk, alg);
64
+ const now = Math.floor(Date.now() / 1e3);
65
+ return new import_jose.SignJWT({}).setProtectedHeader({ alg, typ: "JWT", kid: args.privateJwk.kid }).setIssuer(args.clientId).setSubject(args.clientId).setAudience(`${args.gatewayUrl}/v1/token-exchange`).setIssuedAt(now).setExpirationTime(now + 60).setJti((0, import_node_crypto.randomUUID)()).sign(key);
66
+ }
67
+
68
+ // src/auth-client/types.ts
69
+ var PafiAuthError = class extends Error {
70
+ constructor(message, status, code, correlationId) {
71
+ super(message);
72
+ this.status = status;
73
+ this.code = code;
74
+ this.correlationId = correlationId;
75
+ this.name = "PafiAuthError";
76
+ }
77
+ status;
78
+ code;
79
+ correlationId;
80
+ };
81
+
82
+ // src/auth-client/pafi-auth-client.ts
83
+ var PafiAuthClient = class {
84
+ constructor(opts) {
85
+ this.opts = opts;
86
+ if (!opts.clientPrivateJwk.kid) {
87
+ throw new Error(
88
+ "PafiAuthClient: clientPrivateJwk.kid is required (gateway uses kid to look up the verification key)"
89
+ );
90
+ }
91
+ this.fetchImpl = opts.fetchImpl ?? fetch;
92
+ this.tokenExchangeAud = `${opts.gatewayUrl}/v1/token-exchange`;
93
+ }
94
+ opts;
95
+ fetchImpl;
96
+ tokenExchangeAud;
97
+ // ───────────────────────────────────────────────────────────────
98
+ // EMAIL OTP — 2-step
99
+ // ───────────────────────────────────────────────────────────────
100
+ /**
101
+ * Step 1: ask the gateway to send the user an OTP. Returns the
102
+ * `challengeId` to echo back on {@link verifyEmail}.
103
+ */
104
+ async startEmail(args) {
105
+ const res = await this.post(
106
+ "/v1/auth/email/start",
107
+ {
108
+ issuer_id: this.opts.issuerId,
109
+ email: args.email
110
+ },
111
+ args.correlationId
112
+ );
113
+ return {
114
+ challengeId: res.challenge_id,
115
+ expiresInSec: res.expires_in
116
+ };
117
+ }
118
+ /**
119
+ * Step 2: submit the OTP the user received. On success returns
120
+ * {@link AuthSuccess} containing BOTH the long-lived
121
+ * pafi_session_token (issuer verifies via gateway JWKS) AND the
122
+ * short-lived pafi_jwt (issuer FE feeds to Privy).
123
+ */
124
+ async verifyEmail(args) {
125
+ const res = await this.post(
126
+ "/v1/auth/email/verify",
127
+ {
128
+ challenge_id: args.challengeId,
129
+ otp_code: args.otpCode
130
+ },
131
+ args.correlationId
132
+ );
133
+ return mapAuthSuccess(res);
134
+ }
135
+ // ───────────────────────────────────────────────────────────────
136
+ // GOOGLE — 1-step exchange
137
+ // ───────────────────────────────────────────────────────────────
138
+ /**
139
+ * Hand the gateway an id_token the issuer FE obtained from Google
140
+ * Identity Services (using PAFI's shared client_id). Gateway verifies
141
+ * signature + audience + `email_verified` before resolving identity.
142
+ */
143
+ async exchangeGoogle(args) {
144
+ const res = await this.post(
145
+ "/v1/auth/google/exchange",
146
+ {
147
+ issuer_id: this.opts.issuerId,
148
+ id_token: args.idToken
149
+ },
150
+ args.correlationId
151
+ );
152
+ return mapAuthSuccess(res);
153
+ }
154
+ // ───────────────────────────────────────────────────────────────
155
+ // KAKAO — 1-step exchange (authorization code)
156
+ // ───────────────────────────────────────────────────────────────
157
+ /**
158
+ * Hand the gateway the authorization code returned by Kakao's
159
+ * redirect. Gateway exchanges with Kakao (server-to-server using
160
+ * PAFI's client_secret), verifies id_token, resolves identity.
161
+ *
162
+ * `redirectUri` must match the URL the FE used when starting the
163
+ * Kakao flow. Falls back to the gateway's KAKAO_REDIRECT_URI when
164
+ * omitted — pass an explicit value for multi-environment FEs.
165
+ */
166
+ async exchangeKakao(args) {
167
+ const res = await this.post(
168
+ "/v1/auth/kakao/exchange",
169
+ {
170
+ issuer_id: this.opts.issuerId,
171
+ code: args.code,
172
+ ...args.redirectUri ? { redirect_uri: args.redirectUri } : {}
173
+ },
174
+ args.correlationId
175
+ );
176
+ return mapAuthSuccess(res);
177
+ }
178
+ // ───────────────────────────────────────────────────────────────
179
+ async post(path, body, correlationId) {
180
+ const assertion = await signClientAssertion({
181
+ gatewayUrl: this.opts.gatewayUrl,
182
+ clientId: this.opts.clientId,
183
+ privateJwk: this.opts.clientPrivateJwk,
184
+ alg: this.opts.alg
185
+ });
186
+ const finalCorrelationId = correlationId ?? `iss-${(0, import_node_crypto2.randomUUID)()}`;
187
+ const res = await this.fetchImpl(`${this.opts.gatewayUrl}${path}`, {
188
+ method: "POST",
189
+ headers: {
190
+ Authorization: `Bearer ${assertion}`,
191
+ "Content-Type": "application/json",
192
+ "X-Correlation-Id": finalCorrelationId
193
+ },
194
+ body: JSON.stringify(body)
195
+ });
196
+ const text = await res.text();
197
+ let parsed;
198
+ try {
199
+ parsed = text ? JSON.parse(text) : {};
200
+ } catch {
201
+ throw new PafiAuthError(
202
+ `Non-JSON response from gateway (${path}): ${text.slice(0, 120)}`,
203
+ res.status,
204
+ "non_json_response",
205
+ finalCorrelationId
206
+ );
207
+ }
208
+ if (!res.ok) {
209
+ const err = parsed;
210
+ throw new PafiAuthError(
211
+ err.error_description ?? err.error ?? `Gateway returned HTTP ${res.status}`,
212
+ res.status,
213
+ err.error ?? "unknown_error",
214
+ err.correlation_id ?? finalCorrelationId
215
+ );
216
+ }
217
+ return parsed;
218
+ }
219
+ };
220
+ function mapAuthSuccess(res) {
221
+ return {
222
+ pafiSessionToken: res.pafi_session_token,
223
+ pafiJwt: res.pafi_jwt,
224
+ canonicalId: res.canonical_id,
225
+ expiresAt: res.expires_at,
226
+ isFirstLogin: res.is_first_login,
227
+ ...res.verified_email ? { verifiedEmail: res.verified_email } : {}
228
+ };
229
+ }
230
+
231
+ // src/direct-auth/pafi-direct-auth.module-options.ts
232
+ var PAFI_DIRECT_AUTH_MODULE_OPTIONS = /* @__PURE__ */ Symbol(
233
+ "PAFI_DIRECT_AUTH_MODULE_OPTIONS"
234
+ );
235
+
236
+ // src/direct-auth/services/pafi-auth-client.provider.ts
237
+ var PafiAuthClientProvider = class {
238
+ constructor(options) {
239
+ this.options = options;
240
+ }
241
+ options;
242
+ _client;
243
+ onModuleInit() {
244
+ const jwk = this.options.clientPrivateJwk;
245
+ if (!jwk.kid) {
246
+ throw new Error(
247
+ "PafiDirectAuthModule: clientPrivateJwk.kid is required \u2014 gateway uses kid for key lookup"
248
+ );
249
+ }
250
+ this._client = new PafiAuthClient({
251
+ gatewayUrl: this.options.gatewayUrl,
252
+ issuerId: this.options.issuerId,
253
+ clientId: this.options.clientId,
254
+ clientPrivateJwk: jwk
255
+ });
256
+ }
257
+ get client() {
258
+ return this._client;
259
+ }
260
+ };
261
+ PafiAuthClientProvider = __decorateClass([
262
+ (0, import_common.Injectable)(),
263
+ __decorateParam(0, (0, import_common.Inject)(PAFI_DIRECT_AUTH_MODULE_OPTIONS))
264
+ ], PafiAuthClientProvider);
265
+
266
+ // src/direct-auth/services/pafi-session-verifier.service.ts
267
+ var import_common2 = require("@nestjs/common");
268
+ var import_jose2 = require("jose");
269
+ var PafiSessionVerifierService = class {
270
+ jwks;
271
+ expectedIssuer;
272
+ constructor(options) {
273
+ this.jwks = (0, import_jose2.createRemoteJWKSet)(
274
+ new URL(`${options.gatewayUrl}/.well-known/jwks.json`)
275
+ );
276
+ this.expectedIssuer = options.gatewayUrl;
277
+ }
278
+ async verify(token) {
279
+ let payload;
280
+ try {
281
+ ({ payload } = await (0, import_jose2.jwtVerify)(token, this.jwks, {
282
+ issuer: this.expectedIssuer
283
+ }));
284
+ } catch (err) {
285
+ throw new import_common2.UnauthorizedException(
286
+ `Invalid pafi_session_token: ${err.message}`
287
+ );
288
+ }
289
+ if (payload.scope !== "pafi-session") {
290
+ throw new import_common2.UnauthorizedException(
291
+ `pafi_session_token has wrong scope: ${String(payload.scope)}`
292
+ );
293
+ }
294
+ if (typeof payload.sub !== "string") {
295
+ throw new import_common2.UnauthorizedException("pafi_session_token missing sub");
296
+ }
297
+ if (typeof payload.exp !== "number" || typeof payload.iat !== "number") {
298
+ throw new import_common2.UnauthorizedException("pafi_session_token missing iat/exp");
299
+ }
300
+ const verifiedAttribute = parseVerifiedAttribute(
301
+ payload.verified_attribute
302
+ );
303
+ return {
304
+ sub: payload.sub,
305
+ scope: "pafi-session",
306
+ verifiedAttribute,
307
+ issuerId: typeof payload.issuer_id === "string" ? payload.issuer_id : void 0,
308
+ exp: payload.exp,
309
+ iat: payload.iat,
310
+ raw: payload
311
+ };
312
+ }
313
+ };
314
+ PafiSessionVerifierService = __decorateClass([
315
+ (0, import_common2.Injectable)(),
316
+ __decorateParam(0, (0, import_common2.Inject)(PAFI_DIRECT_AUTH_MODULE_OPTIONS))
317
+ ], PafiSessionVerifierService);
318
+ function parseVerifiedAttribute(raw) {
319
+ if (!raw || typeof raw !== "object") return void 0;
320
+ const obj = raw;
321
+ if (typeof obj.type !== "string") return void 0;
322
+ return {
323
+ type: obj.type,
324
+ valueHash: typeof obj.value_hash === "string" ? obj.value_hash : void 0
325
+ };
326
+ }
327
+
328
+ // src/direct-auth/services/pafi-direct-auth.service.ts
329
+ var import_common3 = require("@nestjs/common");
330
+
331
+ // src/direct-auth/interfaces/user-store.interface.ts
332
+ var USER_STORE = /* @__PURE__ */ Symbol("USER_STORE");
333
+
334
+ // src/direct-auth/interfaces/session-token-minter.interface.ts
335
+ var SESSION_TOKEN_MINTER = /* @__PURE__ */ Symbol("SESSION_TOKEN_MINTER");
336
+
337
+ // src/direct-auth/services/pafi-direct-auth.service.ts
338
+ var PafiDirectAuthService = class {
339
+ constructor(clientProvider, sessionVerifier, userStore, sessionTokenMinter) {
340
+ this.clientProvider = clientProvider;
341
+ this.sessionVerifier = sessionVerifier;
342
+ this.userStore = userStore;
343
+ this.sessionTokenMinter = sessionTokenMinter;
344
+ }
345
+ clientProvider;
346
+ sessionVerifier;
347
+ userStore;
348
+ sessionTokenMinter;
349
+ logger = new import_common3.Logger(PafiDirectAuthService.name);
350
+ // ── Email OTP ────────────────────────────────────────────────────
351
+ async startEmail(args) {
352
+ return this.clientProvider.client.startEmail({
353
+ email: args.email,
354
+ correlationId: args.correlationId
355
+ });
356
+ }
357
+ async verifyEmail(args) {
358
+ const success = await this.clientProvider.client.verifyEmail({
359
+ challengeId: args.challengeId,
360
+ otpCode: args.otpCode,
361
+ correlationId: args.correlationId
362
+ });
363
+ await this.sessionVerifier.verify(success.pafiSessionToken);
364
+ return this.finalize(success);
365
+ }
366
+ // ── Google ───────────────────────────────────────────────────────
367
+ async exchangeGoogle(args) {
368
+ const success = await this.clientProvider.client.exchangeGoogle({
369
+ idToken: args.idToken,
370
+ correlationId: args.correlationId
371
+ });
372
+ await this.sessionVerifier.verify(success.pafiSessionToken);
373
+ return this.finalize(success);
374
+ }
375
+ // ── Kakao ────────────────────────────────────────────────────────
376
+ async exchangeKakao(args) {
377
+ const success = await this.clientProvider.client.exchangeKakao({
378
+ code: args.code,
379
+ redirectUri: args.redirectUri,
380
+ correlationId: args.correlationId
381
+ });
382
+ await this.sessionVerifier.verify(success.pafiSessionToken);
383
+ return this.finalize(success);
384
+ }
385
+ // ── Internal: upsert user + mint issuer session token ───────────
386
+ async finalize(success) {
387
+ await this.userStore.upsertByCanonicalAndEmail({
388
+ canonicalId: success.canonicalId,
389
+ verifiedEmail: success.verifiedEmail
390
+ });
391
+ const { token, expiresAt } = await this.sessionTokenMinter.mint({
392
+ canonicalId: success.canonicalId,
393
+ verifiedEmail: success.verifiedEmail
394
+ });
395
+ return {
396
+ sessionToken: token,
397
+ sessionExpiresAt: expiresAt,
398
+ pafiJwt: success.pafiJwt,
399
+ pafiSessionToken: success.pafiSessionToken,
400
+ canonicalId: success.canonicalId,
401
+ isFirstLogin: success.isFirstLogin,
402
+ ...success.verifiedEmail ? { verifiedEmail: success.verifiedEmail } : {}
403
+ };
404
+ }
405
+ };
406
+ PafiDirectAuthService = __decorateClass([
407
+ (0, import_common3.Injectable)(),
408
+ __decorateParam(2, (0, import_common3.Inject)(USER_STORE)),
409
+ __decorateParam(3, (0, import_common3.Inject)(SESSION_TOKEN_MINTER))
410
+ ], PafiDirectAuthService);
411
+
412
+ // src/direct-auth/pafi-direct-auth.controller.ts
413
+ var import_common4 = require("@nestjs/common");
414
+ var import_swagger2 = require("@nestjs/swagger");
415
+
416
+ // src/direct-auth/pafi-direct-auth.dto.ts
417
+ var import_swagger = require("@nestjs/swagger");
418
+ var import_class_validator = require("class-validator");
419
+ var EmailStartRequestDto = class {
420
+ email;
421
+ };
422
+ __decorateClass([
423
+ (0, import_swagger.ApiProperty)({ example: "user1@example.com" }),
424
+ (0, import_class_validator.IsEmail)(),
425
+ (0, import_class_validator.MaxLength)(320)
426
+ ], EmailStartRequestDto.prototype, "email", 2);
427
+ var EmailVerifyRequestDto = class {
428
+ challengeId;
429
+ otpCode;
430
+ };
431
+ __decorateClass([
432
+ (0, import_swagger.ApiProperty)({
433
+ description: "Challenge id returned by POST /auth/v2/email/start. Opaque to the FE; echo verbatim."
434
+ }),
435
+ (0, import_class_validator.IsString)(),
436
+ (0, import_class_validator.IsNotEmpty)(),
437
+ (0, import_class_validator.MaxLength)(128)
438
+ ], EmailVerifyRequestDto.prototype, "challengeId", 2);
439
+ __decorateClass([
440
+ (0, import_swagger.ApiProperty)({ example: "123456" }),
441
+ (0, import_class_validator.IsString)(),
442
+ (0, import_class_validator.Length)(4, 10)
443
+ ], EmailVerifyRequestDto.prototype, "otpCode", 2);
444
+ var GoogleExchangeRequestDto = class {
445
+ idToken;
446
+ };
447
+ __decorateClass([
448
+ (0, import_swagger.ApiProperty)({
449
+ description: "Google-issued ID token (JWS). Obtain on FE via Google Identity Services using PAFI's Google OAuth client_id."
450
+ }),
451
+ (0, import_class_validator.IsString)(),
452
+ (0, import_class_validator.IsNotEmpty)(),
453
+ (0, import_class_validator.MaxLength)(8192)
454
+ ], GoogleExchangeRequestDto.prototype, "idToken", 2);
455
+ var KakaoExchangeRequestDto = class {
456
+ code;
457
+ redirectUri;
458
+ };
459
+ __decorateClass([
460
+ (0, import_swagger.ApiProperty)({
461
+ description: "Authorization code returned by Kakao to the FE redirect URL."
462
+ }),
463
+ (0, import_class_validator.IsString)(),
464
+ (0, import_class_validator.IsNotEmpty)(),
465
+ (0, import_class_validator.MaxLength)(2048)
466
+ ], KakaoExchangeRequestDto.prototype, "code", 2);
467
+ __decorateClass([
468
+ (0, import_swagger.ApiProperty)({
469
+ description: "Redirect URI the FE used when initiating the Kakao flow. Optional \u2014 gateway falls back to its own KAKAO_REDIRECT_URI env.",
470
+ required: false
471
+ }),
472
+ (0, import_class_validator.IsOptional)(),
473
+ (0, import_class_validator.IsUrl)({ require_tld: false, require_protocol: true }),
474
+ (0, import_class_validator.MaxLength)(2048)
475
+ ], KakaoExchangeRequestDto.prototype, "redirectUri", 2);
476
+ var EmailStartResponseDto = class {
477
+ challengeId;
478
+ expiresInSec;
479
+ };
480
+ __decorateClass([
481
+ (0, import_swagger.ApiProperty)()
482
+ ], EmailStartResponseDto.prototype, "challengeId", 2);
483
+ __decorateClass([
484
+ (0, import_swagger.ApiProperty)({ description: "Seconds until the challenge expires." })
485
+ ], EmailStartResponseDto.prototype, "expiresInSec", 2);
486
+ var PafiAuthSuccessDto = class {
487
+ sessionToken;
488
+ sessionExpiresAt;
489
+ pafiJwt;
490
+ pafiSessionToken;
491
+ canonicalId;
492
+ isFirstLogin;
493
+ verifiedEmail;
494
+ };
495
+ __decorateClass([
496
+ (0, import_swagger.ApiProperty)({
497
+ description: "Issuer-native session token (typically HS256, minted by ISessionTokenMinter) \u2014 Bearer-auth for subsequent issuer API calls."
498
+ })
499
+ ], PafiAuthSuccessDto.prototype, "sessionToken", 2);
500
+ __decorateClass([
501
+ (0, import_swagger.ApiProperty)({ description: "Issuer session token expiration (ISO 8601)." })
502
+ ], PafiAuthSuccessDto.prototype, "sessionExpiresAt", 2);
503
+ __decorateClass([
504
+ (0, import_swagger.ApiProperty)({
505
+ description: "Short-lived PAFI JWT (60s) \u2014 FE feeds verbatim to Privy.loginWithCustomAuth() to provision the embedded wallet."
506
+ })
507
+ ], PafiAuthSuccessDto.prototype, "pafiJwt", 2);
508
+ __decorateClass([
509
+ (0, import_swagger.ApiProperty)({
510
+ description: "Long-lived PAFI session token (24h) \u2014 opaque to FE; keep alongside sessionToken if you ever need to call the gateway directly."
511
+ })
512
+ ], PafiAuthSuccessDto.prototype, "pafiSessionToken", 2);
513
+ __decorateClass([
514
+ (0, import_swagger.ApiProperty)({ description: "canonical_pafi_user_id assigned by the gateway." })
515
+ ], PafiAuthSuccessDto.prototype, "canonicalId", 2);
516
+ __decorateClass([
517
+ (0, import_swagger.ApiProperty)({
518
+ description: "True the first time the user appears at the gateway."
519
+ })
520
+ ], PafiAuthSuccessDto.prototype, "isFirstLogin", 2);
521
+ __decorateClass([
522
+ (0, import_swagger.ApiProperty)({
523
+ description: "Verified email (when the auth method exposed one \u2014 email OTP and Google always; Kakao only if the user shared their email).",
524
+ required: false
525
+ })
526
+ ], PafiAuthSuccessDto.prototype, "verifiedEmail", 2);
527
+
528
+ // src/direct-auth/pafi-direct-auth.controller.ts
529
+ var PafiDirectAuthController = class {
530
+ constructor(directAuth) {
531
+ this.directAuth = directAuth;
532
+ }
533
+ directAuth;
534
+ async startEmail(body) {
535
+ const res = await this.directAuth.startEmail({ email: body.email });
536
+ return {
537
+ challengeId: res.challengeId,
538
+ expiresInSec: res.expiresInSec
539
+ };
540
+ }
541
+ async verifyEmail(body) {
542
+ return this.directAuth.verifyEmail({
543
+ challengeId: body.challengeId,
544
+ otpCode: body.otpCode
545
+ });
546
+ }
547
+ async exchangeGoogle(body) {
548
+ return this.directAuth.exchangeGoogle({ idToken: body.idToken });
549
+ }
550
+ async exchangeKakao(body) {
551
+ return this.directAuth.exchangeKakao({
552
+ code: body.code,
553
+ redirectUri: body.redirectUri
554
+ });
555
+ }
556
+ };
557
+ __decorateClass([
558
+ (0, import_common4.Post)("email/start"),
559
+ (0, import_common4.HttpCode)(import_common4.HttpStatus.OK),
560
+ (0, import_swagger2.ApiOperation)({
561
+ summary: "Step 1: ask gateway to send an OTP to the user email.",
562
+ description: "Gateway generates the OTP, sends it via its configured email provider, and returns an opaque challenge_id. The FE echoes that challenge_id back on step 2 along with the code the user typed."
563
+ }),
564
+ (0, import_swagger2.ApiOkResponse)({ type: EmailStartResponseDto }),
565
+ __decorateParam(0, (0, import_common4.Body)())
566
+ ], PafiDirectAuthController.prototype, "startEmail", 1);
567
+ __decorateClass([
568
+ (0, import_common4.Post)("email/verify"),
569
+ (0, import_common4.HttpCode)(import_common4.HttpStatus.OK),
570
+ (0, import_swagger2.ApiOperation)({
571
+ summary: "Step 2: submit the OTP to complete email sign-in.",
572
+ description: "Gateway verifies the OTP, derives canonical_id from the verified email, and mints both a pafi_session_token (24h, gateway-signed) and pafi_jwt (60s, for Privy.loginWithCustomAuth). Issuer wraps these in a session token of its own (sub = canonical_id) so existing guards keep working."
573
+ }),
574
+ (0, import_swagger2.ApiOkResponse)({ type: PafiAuthSuccessDto }),
575
+ __decorateParam(0, (0, import_common4.Body)())
576
+ ], PafiDirectAuthController.prototype, "verifyEmail", 1);
577
+ __decorateClass([
578
+ (0, import_common4.Post)("google/exchange"),
579
+ (0, import_common4.HttpCode)(import_common4.HttpStatus.OK),
580
+ (0, import_swagger2.ApiOperation)({
581
+ summary: "Sign in with Google.",
582
+ description: "Hand the gateway a Google-issued id_token (FE obtains via Google Identity Services using PAFI's shared client_id). Gateway verifies signature + email_verified, derives canonical_id from the email, returns the same token bundle as /email/verify."
583
+ }),
584
+ (0, import_swagger2.ApiOkResponse)({ type: PafiAuthSuccessDto }),
585
+ __decorateParam(0, (0, import_common4.Body)())
586
+ ], PafiDirectAuthController.prototype, "exchangeGoogle", 1);
587
+ __decorateClass([
588
+ (0, import_common4.Post)("kakao/exchange"),
589
+ (0, import_common4.HttpCode)(import_common4.HttpStatus.OK),
590
+ (0, import_swagger2.ApiOperation)({
591
+ summary: "Sign in with Kakao.",
592
+ description: "Hand the gateway the authorization code Kakao redirected back to the FE. Gateway exchanges with Kakao server-to-server (using PAFI-held client_secret), verifies the id_token, and returns the same token bundle as /email/verify. canonical_id derives from email when present, else from the Kakao sub."
593
+ }),
594
+ (0, import_swagger2.ApiOkResponse)({ type: PafiAuthSuccessDto }),
595
+ __decorateParam(0, (0, import_common4.Body)())
596
+ ], PafiDirectAuthController.prototype, "exchangeKakao", 1);
597
+ PafiDirectAuthController = __decorateClass([
598
+ (0, import_swagger2.ApiTags)("pafi-auth-v2"),
599
+ (0, import_common4.Controller)("auth/v2")
600
+ ], PafiDirectAuthController);
601
+
602
+ // src/direct-auth/pafi-direct-auth.module.ts
603
+ var PafiDirectAuthModule = class {
604
+ static forRoot(options) {
605
+ return {
606
+ module: PafiDirectAuthModule,
607
+ controllers: [PafiDirectAuthController],
608
+ providers: [
609
+ {
610
+ provide: PAFI_DIRECT_AUTH_MODULE_OPTIONS,
611
+ useValue: options
612
+ },
613
+ // Bridge user-supplied implementations into the injection
614
+ // tokens the orchestrator uses. The class itself MUST also be
615
+ // listed in the host's providers array (NestJS only instantiates
616
+ // classes it sees in providers; useExisting requires that).
617
+ {
618
+ provide: USER_STORE,
619
+ useExisting: options.userStore
620
+ },
621
+ {
622
+ provide: SESSION_TOKEN_MINTER,
623
+ useExisting: options.sessionTokenMinter
624
+ },
625
+ PafiAuthClientProvider,
626
+ PafiSessionVerifierService,
627
+ PafiDirectAuthService
628
+ ],
629
+ exports: [
630
+ PafiAuthClientProvider,
631
+ PafiSessionVerifierService,
632
+ PafiDirectAuthService
633
+ ]
634
+ };
635
+ }
636
+ };
637
+ PafiDirectAuthModule = __decorateClass([
638
+ (0, import_common5.Module)({})
639
+ ], PafiDirectAuthModule);
640
+ // Annotate the CommonJS export names for ESM import in node:
641
+ 0 && (module.exports = {
642
+ EmailStartRequestDto,
643
+ EmailStartResponseDto,
644
+ EmailVerifyRequestDto,
645
+ GoogleExchangeRequestDto,
646
+ KakaoExchangeRequestDto,
647
+ PAFI_DIRECT_AUTH_MODULE_OPTIONS,
648
+ PafiAuthClientProvider,
649
+ PafiAuthSuccessDto,
650
+ PafiDirectAuthController,
651
+ PafiDirectAuthModule,
652
+ PafiDirectAuthService,
653
+ PafiSessionVerifierService,
654
+ SESSION_TOKEN_MINTER,
655
+ USER_STORE
656
+ });
657
+ //# sourceMappingURL=index.cjs.map