@pafi-dev/issuer 0.38.1 → 0.39.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/auth-client/index.d.cts +2 -140
- package/dist/auth-client/index.d.ts +2 -140
- package/dist/auth-client/index.js +5 -176
- package/dist/auth-client/index.js.map +1 -1
- package/dist/chunk-7VEYSL2C.js +180 -0
- package/dist/chunk-7VEYSL2C.js.map +1 -0
- package/dist/direct-auth/index.cjs +662 -0
- package/dist/direct-auth/index.cjs.map +1 -0
- package/dist/direct-auth/index.d.cts +421 -0
- package/dist/direct-auth/index.d.ts +421 -0
- package/dist/direct-auth/index.js +465 -0
- package/dist/direct-auth/index.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/pafi-auth-client-DzHd_Ts_.d.cts +142 -0
- package/dist/pafi-auth-client-DzHd_Ts_.d.ts +142 -0
- package/package.json +33 -1
|
@@ -0,0 +1,662 @@
|
|
|
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
|
+
imports: options.imports ?? [],
|
|
608
|
+
controllers: [PafiDirectAuthController],
|
|
609
|
+
providers: [
|
|
610
|
+
{
|
|
611
|
+
provide: PAFI_DIRECT_AUTH_MODULE_OPTIONS,
|
|
612
|
+
useValue: options
|
|
613
|
+
},
|
|
614
|
+
// Register adapter classes IN THIS MODULE'S SCOPE. NestJS then
|
|
615
|
+
// resolves their constructor deps from imports + global
|
|
616
|
+
// providers — which is exactly what the issuer expected to
|
|
617
|
+
// happen via `useExisting`, but `useExisting` does NOT cross
|
|
618
|
+
// module boundaries (it's an alias lookup within the module's
|
|
619
|
+
// own provider registry, not a host-tree walk).
|
|
620
|
+
options.userStore,
|
|
621
|
+
options.sessionTokenMinter,
|
|
622
|
+
{
|
|
623
|
+
provide: USER_STORE,
|
|
624
|
+
useExisting: options.userStore
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
provide: SESSION_TOKEN_MINTER,
|
|
628
|
+
useExisting: options.sessionTokenMinter
|
|
629
|
+
},
|
|
630
|
+
PafiAuthClientProvider,
|
|
631
|
+
PafiSessionVerifierService,
|
|
632
|
+
PafiDirectAuthService
|
|
633
|
+
],
|
|
634
|
+
exports: [
|
|
635
|
+
PafiAuthClientProvider,
|
|
636
|
+
PafiSessionVerifierService,
|
|
637
|
+
PafiDirectAuthService
|
|
638
|
+
]
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
PafiDirectAuthModule = __decorateClass([
|
|
643
|
+
(0, import_common5.Module)({})
|
|
644
|
+
], PafiDirectAuthModule);
|
|
645
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
646
|
+
0 && (module.exports = {
|
|
647
|
+
EmailStartRequestDto,
|
|
648
|
+
EmailStartResponseDto,
|
|
649
|
+
EmailVerifyRequestDto,
|
|
650
|
+
GoogleExchangeRequestDto,
|
|
651
|
+
KakaoExchangeRequestDto,
|
|
652
|
+
PAFI_DIRECT_AUTH_MODULE_OPTIONS,
|
|
653
|
+
PafiAuthClientProvider,
|
|
654
|
+
PafiAuthSuccessDto,
|
|
655
|
+
PafiDirectAuthController,
|
|
656
|
+
PafiDirectAuthModule,
|
|
657
|
+
PafiDirectAuthService,
|
|
658
|
+
PafiSessionVerifierService,
|
|
659
|
+
SESSION_TOKEN_MINTER,
|
|
660
|
+
USER_STORE
|
|
661
|
+
});
|
|
662
|
+
//# sourceMappingURL=index.cjs.map
|