@seamless-auth/express 0.1.2 → 0.3.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.
- package/README.md +18 -0
- package/dist/createServer.d.ts +78 -0
- package/dist/createServer.d.ts.map +1 -0
- package/dist/getSeamlessUser.d.ts +4 -0
- package/dist/getSeamlessUser.d.ts.map +1 -0
- package/dist/handlers/admin.d.ts +14 -0
- package/dist/handlers/admin.d.ts.map +1 -0
- package/dist/handlers/bootstrapAdmininvite.d.ts +4 -0
- package/dist/handlers/bootstrapAdmininvite.d.ts.map +1 -0
- package/dist/handlers/finishLogin.d.ts +6 -0
- package/dist/handlers/finishLogin.d.ts.map +1 -0
- package/dist/handlers/finishRegister.d.ts +6 -0
- package/dist/handlers/finishRegister.d.ts.map +1 -0
- package/dist/handlers/internalMetrics.d.ts +9 -0
- package/dist/handlers/internalMetrics.d.ts.map +1 -0
- package/dist/handlers/login.d.ts +4 -0
- package/dist/handlers/login.d.ts.map +1 -0
- package/dist/handlers/logout.d.ts +4 -0
- package/dist/handlers/logout.d.ts.map +1 -0
- package/dist/handlers/me.d.ts +6 -0
- package/dist/handlers/me.d.ts.map +1 -0
- package/dist/handlers/pollMagicLinkConfirmation.d.ts +6 -0
- package/dist/handlers/pollMagicLinkConfirmation.d.ts.map +1 -0
- package/dist/handlers/register.d.ts +4 -0
- package/dist/handlers/register.d.ts.map +1 -0
- package/dist/handlers/requestMagicLink.d.ts +7 -0
- package/dist/handlers/requestMagicLink.d.ts.map +1 -0
- package/dist/handlers/requestOtp.d.ts +7 -0
- package/dist/handlers/requestOtp.d.ts.map +1 -0
- package/dist/handlers/sessions.d.ts +6 -0
- package/dist/handlers/sessions.d.ts.map +1 -0
- package/dist/handlers/systemConfig.d.ts +6 -0
- package/dist/handlers/systemConfig.d.ts.map +1 -0
- package/dist/index.d.ts +9 -197
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +621 -12
- package/dist/internal/buildAuthorization.d.ts +6 -0
- package/dist/internal/buildAuthorization.d.ts.map +1 -0
- package/dist/internal/cookie.d.ts +17 -0
- package/dist/internal/cookie.d.ts.map +1 -0
- package/dist/internal/deliverAuthMessage.d.ts +6 -0
- package/dist/internal/deliverAuthMessage.d.ts.map +1 -0
- package/dist/messaging.d.ts +98 -0
- package/dist/messaging.d.ts.map +1 -0
- package/dist/middleware/ensureCookies.d.ts +16 -0
- package/dist/middleware/ensureCookies.d.ts.map +1 -0
- package/dist/middleware/requireAuth.d.ts +69 -0
- package/dist/middleware/requireAuth.d.ts.map +1 -0
- package/dist/middleware/requireRole.d.ts +36 -0
- package/dist/middleware/requireRole.d.ts.map +1 -0
- package/package.json +7 -5
package/dist/index.js
CHANGED
|
@@ -139,11 +139,12 @@ import { finishLoginHandler } from "@seamless-auth/core/handlers/finishLogin";
|
|
|
139
139
|
// src/internal/buildAuthorization.ts
|
|
140
140
|
import { createServiceToken } from "@seamless-auth/core";
|
|
141
141
|
function buildServiceAuthorization(req, opts) {
|
|
142
|
-
|
|
142
|
+
const subject = req.cookiePayload?.sub || req.user?.sub;
|
|
143
|
+
if (!subject) {
|
|
143
144
|
return void 0;
|
|
144
145
|
}
|
|
145
146
|
const token = createServiceToken({
|
|
146
|
-
subject
|
|
147
|
+
subject,
|
|
147
148
|
issuer: opts.issuer,
|
|
148
149
|
audience: opts.audience,
|
|
149
150
|
serviceSecret: opts.serviceSecret,
|
|
@@ -194,6 +195,174 @@ async function finishLogin(req, res, opts) {
|
|
|
194
195
|
|
|
195
196
|
// src/handlers/register.ts
|
|
196
197
|
import { registerHandler } from "@seamless-auth/core/handlers/register";
|
|
198
|
+
|
|
199
|
+
// src/internal/deliverAuthMessage.ts
|
|
200
|
+
function applyEmailOverride(override, input, defaults, appName) {
|
|
201
|
+
return override ? override(input, defaults, { appName }) : defaults;
|
|
202
|
+
}
|
|
203
|
+
function applySmsOverride(override, input, defaults, appName) {
|
|
204
|
+
return override ? override(input, defaults, { appName }) : defaults;
|
|
205
|
+
}
|
|
206
|
+
function buildOtpEmailMessage(input, messaging) {
|
|
207
|
+
const appName = messaging.defaults?.appName ?? "Seamless Auth";
|
|
208
|
+
return applyEmailOverride(
|
|
209
|
+
messaging.overrides?.otpEmail,
|
|
210
|
+
{
|
|
211
|
+
to: input.to,
|
|
212
|
+
token: input.token,
|
|
213
|
+
from: messaging.defaults?.emailFrom,
|
|
214
|
+
subject: `${appName} - Verify your email`
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
to: input.to,
|
|
218
|
+
from: messaging.defaults?.emailFrom,
|
|
219
|
+
subject: `${appName} - Verify your email`,
|
|
220
|
+
text: `Verify your account with ${appName}.
|
|
221
|
+
|
|
222
|
+
Your verification code is: ${input.token}
|
|
223
|
+
|
|
224
|
+
If you did not request this code, you can safely ignore this message.`,
|
|
225
|
+
html: `<div><h1>Verify your account with ${appName}</h1><p>Please use the verification code below:</p><p><strong>${input.token}</strong></p><p>If you did not request this code, you can safely ignore this message.</p></div>`
|
|
226
|
+
},
|
|
227
|
+
appName
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
function buildOtpSmsMessage(input, messaging) {
|
|
231
|
+
const appName = messaging.defaults?.appName ?? "Seamless Auth";
|
|
232
|
+
return applySmsOverride(
|
|
233
|
+
messaging.overrides?.otpSms,
|
|
234
|
+
{
|
|
235
|
+
to: input.to,
|
|
236
|
+
token: input.token,
|
|
237
|
+
from: messaging.defaults?.smsFrom
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
to: input.to,
|
|
241
|
+
from: messaging.defaults?.smsFrom,
|
|
242
|
+
body: `Your ${appName} verification code is: ${input.token}. No one will ever ask you for this code. Do not share it.`
|
|
243
|
+
},
|
|
244
|
+
appName
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
function buildMagicLinkMessage(input, messaging) {
|
|
248
|
+
const appName = messaging.defaults?.appName ?? "Seamless Auth";
|
|
249
|
+
return applyEmailOverride(
|
|
250
|
+
messaging.overrides?.magicLinkEmail,
|
|
251
|
+
{
|
|
252
|
+
to: input.to,
|
|
253
|
+
magicLinkUrl: input.magicLinkUrl,
|
|
254
|
+
token: input.token,
|
|
255
|
+
from: messaging.defaults?.emailFrom,
|
|
256
|
+
subject: `${appName} - Your sign-in link`
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
to: input.to,
|
|
260
|
+
from: messaging.defaults?.emailFrom,
|
|
261
|
+
subject: `${appName} - Your sign-in link`,
|
|
262
|
+
text: `Use the link below to sign in to ${appName}:
|
|
263
|
+
|
|
264
|
+
${input.magicLinkUrl}
|
|
265
|
+
|
|
266
|
+
If you did not request this email, you can safely ignore it.`,
|
|
267
|
+
html: `<div><h1>Sign in to ${appName}</h1><p>Use the link below to complete sign-in:</p><p><a href="${input.magicLinkUrl}">${input.magicLinkUrl}</a></p><p>If you did not request this email, you can safely ignore it.</p></div>`
|
|
268
|
+
},
|
|
269
|
+
appName
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
function buildBootstrapInviteMessage(input, messaging) {
|
|
273
|
+
const appName = messaging.defaults?.appName ?? "Seamless Auth";
|
|
274
|
+
return applyEmailOverride(
|
|
275
|
+
messaging.overrides?.bootstrapInviteEmail,
|
|
276
|
+
{
|
|
277
|
+
to: input.to,
|
|
278
|
+
inviteUrl: input.inviteUrl,
|
|
279
|
+
token: input.token,
|
|
280
|
+
from: messaging.defaults?.emailFrom,
|
|
281
|
+
subject: `${appName} - Bootstrap invite`
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
to: input.to,
|
|
285
|
+
from: messaging.defaults?.emailFrom,
|
|
286
|
+
subject: `${appName} - Bootstrap invite`,
|
|
287
|
+
text: `You have been invited to bootstrap ${appName}.
|
|
288
|
+
|
|
289
|
+
Use the link below to continue:
|
|
290
|
+
${input.inviteUrl}`,
|
|
291
|
+
html: `<div><h1>Bootstrap invite for ${appName}</h1><p>Use the link below to continue:</p><p><a href="${input.inviteUrl}">${input.inviteUrl}</a></p></div>`
|
|
292
|
+
},
|
|
293
|
+
appName
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
async function deliverAuthMessage(messaging, delivery) {
|
|
297
|
+
if (!messaging || !delivery) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
switch (delivery.kind) {
|
|
301
|
+
case "otp_email":
|
|
302
|
+
if (messaging.handlers?.sendOtpEmail) {
|
|
303
|
+
await messaging.handlers.sendOtpEmail({
|
|
304
|
+
to: delivery.to,
|
|
305
|
+
token: delivery.token,
|
|
306
|
+
from: messaging.defaults?.emailFrom
|
|
307
|
+
});
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (!messaging.email) {
|
|
311
|
+
throw new Error("Missing email transport for OTP email delivery.");
|
|
312
|
+
}
|
|
313
|
+
await messaging.email.send(buildOtpEmailMessage(delivery, messaging));
|
|
314
|
+
return;
|
|
315
|
+
case "otp_sms":
|
|
316
|
+
if (messaging.handlers?.sendOtpSms) {
|
|
317
|
+
await messaging.handlers.sendOtpSms({
|
|
318
|
+
to: delivery.to,
|
|
319
|
+
token: delivery.token,
|
|
320
|
+
from: messaging.defaults?.smsFrom
|
|
321
|
+
});
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (!messaging.sms) {
|
|
325
|
+
throw new Error("Missing SMS transport for OTP SMS delivery.");
|
|
326
|
+
}
|
|
327
|
+
await messaging.sms.send(buildOtpSmsMessage(delivery, messaging));
|
|
328
|
+
return;
|
|
329
|
+
case "magic_link_email":
|
|
330
|
+
if (messaging.handlers?.sendMagicLinkEmail) {
|
|
331
|
+
await messaging.handlers.sendMagicLinkEmail({
|
|
332
|
+
to: delivery.to,
|
|
333
|
+
token: delivery.token,
|
|
334
|
+
magicLinkUrl: delivery.magicLinkUrl,
|
|
335
|
+
from: messaging.defaults?.emailFrom
|
|
336
|
+
});
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (!messaging.email) {
|
|
340
|
+
throw new Error("Missing email transport for magic link delivery.");
|
|
341
|
+
}
|
|
342
|
+
await messaging.email.send(buildMagicLinkMessage(delivery, messaging));
|
|
343
|
+
return;
|
|
344
|
+
case "bootstrap_invite_email":
|
|
345
|
+
if (messaging.handlers?.sendBootstrapInviteEmail) {
|
|
346
|
+
await messaging.handlers.sendBootstrapInviteEmail({
|
|
347
|
+
to: delivery.to,
|
|
348
|
+
inviteUrl: delivery.inviteUrl,
|
|
349
|
+
from: messaging.defaults?.emailFrom
|
|
350
|
+
});
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (!messaging.email) {
|
|
354
|
+
throw new Error("Missing email transport for bootstrap invite delivery.");
|
|
355
|
+
}
|
|
356
|
+
await messaging.email.send(buildBootstrapInviteMessage(delivery, messaging));
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function stripDelivery(body) {
|
|
361
|
+
const { delivery: _delivery, ...rest } = body;
|
|
362
|
+
return rest;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// src/handlers/register.ts
|
|
197
366
|
async function register(req, res, opts) {
|
|
198
367
|
const cookieSigner = {
|
|
199
368
|
secret: opts.cookieSecret,
|
|
@@ -205,7 +374,8 @@ async function register(req, res, opts) {
|
|
|
205
374
|
{
|
|
206
375
|
authServerUrl: opts.authServerUrl,
|
|
207
376
|
cookieDomain: opts.cookieDomain,
|
|
208
|
-
registrationCookieName: opts.registrationCookieName
|
|
377
|
+
registrationCookieName: opts.registrationCookieName,
|
|
378
|
+
externalDelivery: Boolean(opts.messaging)
|
|
209
379
|
}
|
|
210
380
|
);
|
|
211
381
|
if (!cookieSigner.secret) {
|
|
@@ -216,9 +386,9 @@ async function register(req, res, opts) {
|
|
|
216
386
|
setSessionCookie(
|
|
217
387
|
res,
|
|
218
388
|
{
|
|
219
|
-
name:
|
|
389
|
+
name: c.name,
|
|
220
390
|
payload: c.value,
|
|
221
|
-
domain: c.domain,
|
|
391
|
+
domain: c.domain ?? opts.cookieDomain,
|
|
222
392
|
ttlSeconds: c.ttl
|
|
223
393
|
},
|
|
224
394
|
cookieSigner
|
|
@@ -228,11 +398,45 @@ async function register(req, res, opts) {
|
|
|
228
398
|
if (result.error) {
|
|
229
399
|
return res.status(result.status).json(result.error);
|
|
230
400
|
}
|
|
401
|
+
if (result.body && typeof result.body === "object" && "delivery" in result.body) {
|
|
402
|
+
await deliverAuthMessage(
|
|
403
|
+
opts.messaging,
|
|
404
|
+
result.body.delivery
|
|
405
|
+
);
|
|
406
|
+
return res.status(result.status).json(stripDelivery(result.body)).end();
|
|
407
|
+
}
|
|
231
408
|
res.status(result.status).json(result.body).end();
|
|
232
409
|
}
|
|
233
410
|
|
|
411
|
+
// src/handlers/requestOtp.ts
|
|
412
|
+
import { requestOtpHandler } from "@seamless-auth/core/handlers/requestOtpHandler";
|
|
413
|
+
async function requestOtp(req, res, opts, kind) {
|
|
414
|
+
const result = await requestOtpHandler(
|
|
415
|
+
{
|
|
416
|
+
kind,
|
|
417
|
+
authorization: buildServiceAuthorization(req, opts)
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
authServerUrl: opts.authServerUrl,
|
|
421
|
+
externalDelivery: Boolean(opts.messaging)
|
|
422
|
+
}
|
|
423
|
+
);
|
|
424
|
+
if (result.error) {
|
|
425
|
+
return res.status(result.status).json(result.error);
|
|
426
|
+
}
|
|
427
|
+
if (result.body && typeof result.body === "object" && "delivery" in result.body) {
|
|
428
|
+
await deliverAuthMessage(
|
|
429
|
+
opts.messaging,
|
|
430
|
+
result.body.delivery
|
|
431
|
+
);
|
|
432
|
+
return res.status(result.status).json(stripDelivery(result.body));
|
|
433
|
+
}
|
|
434
|
+
return res.status(result.status).json(result.body);
|
|
435
|
+
}
|
|
436
|
+
|
|
234
437
|
// src/handlers/finishRegister.ts
|
|
235
438
|
import { finishRegisterHandler } from "@seamless-auth/core/handlers/finishRegister";
|
|
439
|
+
import { verifyCookieJwt } from "@seamless-auth/core";
|
|
236
440
|
async function finishRegister(req, res, opts) {
|
|
237
441
|
const cookieSigner = {
|
|
238
442
|
secret: opts.cookieSecret,
|
|
@@ -240,8 +444,24 @@ async function finishRegister(req, res, opts) {
|
|
|
240
444
|
sameSite: process.env.NODE_ENV === "production" ? "none" : "lax"
|
|
241
445
|
};
|
|
242
446
|
const authorization = buildServiceAuthorization(req, opts);
|
|
447
|
+
const bootstrapToken = req.cookies?.["seamless_bootstrap_token"];
|
|
448
|
+
const headers = {};
|
|
449
|
+
if (bootstrapToken) {
|
|
450
|
+
const payload = verifyCookieJwt(bootstrapToken, opts.cookieSecret);
|
|
451
|
+
if (!payload || !payload.sub) {
|
|
452
|
+
res.status(401).json({
|
|
453
|
+
error: "Invalid or expired session"
|
|
454
|
+
});
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
headers["cookie"] = `seamless_bootstrap_token=${payload.sub}`;
|
|
458
|
+
}
|
|
243
459
|
const result = await finishRegisterHandler(
|
|
244
|
-
{
|
|
460
|
+
{
|
|
461
|
+
body: req.body,
|
|
462
|
+
authorization,
|
|
463
|
+
headers
|
|
464
|
+
},
|
|
245
465
|
{
|
|
246
466
|
authServerUrl: opts.authServerUrl,
|
|
247
467
|
cookieDomain: opts.cookieDomain,
|
|
@@ -346,10 +566,305 @@ async function pollMagicLinkConfirmation(req, res, opts) {
|
|
|
346
566
|
res.status(result.status).json(result.body).end();
|
|
347
567
|
}
|
|
348
568
|
|
|
569
|
+
// src/handlers/requestMagicLink.ts
|
|
570
|
+
import { requestMagicLinkHandler } from "@seamless-auth/core/handlers/requestMagicLinkHandler";
|
|
571
|
+
async function requestMagicLink(req, res, opts) {
|
|
572
|
+
const result = await requestMagicLinkHandler(
|
|
573
|
+
{
|
|
574
|
+
authorization: buildServiceAuthorization(req, opts)
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
authServerUrl: opts.authServerUrl,
|
|
578
|
+
externalDelivery: Boolean(opts.messaging)
|
|
579
|
+
}
|
|
580
|
+
);
|
|
581
|
+
if (result.error) {
|
|
582
|
+
return res.status(result.status).json(result.error);
|
|
583
|
+
}
|
|
584
|
+
if (result.body && typeof result.body === "object" && "delivery" in result.body) {
|
|
585
|
+
await deliverAuthMessage(
|
|
586
|
+
opts.messaging,
|
|
587
|
+
result.body.delivery
|
|
588
|
+
);
|
|
589
|
+
return res.status(result.status).json(stripDelivery(result.body));
|
|
590
|
+
}
|
|
591
|
+
return res.status(result.status).json(result.body);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// src/handlers/admin.ts
|
|
595
|
+
import {
|
|
596
|
+
getUsersHandler,
|
|
597
|
+
createUserHandler,
|
|
598
|
+
deleteUserHandler,
|
|
599
|
+
updateUserHandler,
|
|
600
|
+
getUserDetailHandler,
|
|
601
|
+
getUserAnomaliesHandler,
|
|
602
|
+
getAuthEventsHandler,
|
|
603
|
+
getCredentialCountHandler,
|
|
604
|
+
listAllSessionsHandler,
|
|
605
|
+
listUserSessionsHandler,
|
|
606
|
+
revokeAllUserSessionsHandler
|
|
607
|
+
} from "@seamless-auth/core/handlers/admin";
|
|
608
|
+
function handle(res, result) {
|
|
609
|
+
if (result.error) {
|
|
610
|
+
return res.status(result.status).json({ error: result.error });
|
|
611
|
+
}
|
|
612
|
+
return res.status(result.status).json(result.body);
|
|
613
|
+
}
|
|
614
|
+
var getUsers = async (req, res, opts) => handle(
|
|
615
|
+
res,
|
|
616
|
+
await getUsersHandler({
|
|
617
|
+
authServerUrl: opts.authServerUrl,
|
|
618
|
+
authorization: buildServiceAuthorization(req, opts)
|
|
619
|
+
})
|
|
620
|
+
);
|
|
621
|
+
var createUser = async (req, res, opts) => handle(
|
|
622
|
+
res,
|
|
623
|
+
await createUserHandler({
|
|
624
|
+
authServerUrl: opts.authServerUrl,
|
|
625
|
+
authorization: buildServiceAuthorization(req, opts),
|
|
626
|
+
body: req.body
|
|
627
|
+
})
|
|
628
|
+
);
|
|
629
|
+
var deleteUser = async (req, res, opts) => handle(
|
|
630
|
+
res,
|
|
631
|
+
await deleteUserHandler({
|
|
632
|
+
authServerUrl: opts.authServerUrl,
|
|
633
|
+
authorization: buildServiceAuthorization(req, opts)
|
|
634
|
+
})
|
|
635
|
+
);
|
|
636
|
+
var updateUser = async (req, res, opts) => handle(
|
|
637
|
+
res,
|
|
638
|
+
await updateUserHandler(req.params.userId, {
|
|
639
|
+
authServerUrl: opts.authServerUrl,
|
|
640
|
+
authorization: buildServiceAuthorization(req, opts),
|
|
641
|
+
body: req.body
|
|
642
|
+
})
|
|
643
|
+
);
|
|
644
|
+
var getUserDetail = async (req, res, opts) => handle(
|
|
645
|
+
res,
|
|
646
|
+
await getUserDetailHandler(req.params.userId, {
|
|
647
|
+
authServerUrl: opts.authServerUrl,
|
|
648
|
+
authorization: buildServiceAuthorization(req, opts)
|
|
649
|
+
})
|
|
650
|
+
);
|
|
651
|
+
var getUserAnomalies = async (req, res, opts) => handle(
|
|
652
|
+
res,
|
|
653
|
+
await getUserAnomaliesHandler(req.params.userId, {
|
|
654
|
+
authServerUrl: opts.authServerUrl,
|
|
655
|
+
authorization: buildServiceAuthorization(req, opts)
|
|
656
|
+
})
|
|
657
|
+
);
|
|
658
|
+
var getAuthEvents = async (req, res, opts) => handle(
|
|
659
|
+
res,
|
|
660
|
+
await getAuthEventsHandler({
|
|
661
|
+
authServerUrl: opts.authServerUrl,
|
|
662
|
+
authorization: buildServiceAuthorization(req, opts),
|
|
663
|
+
query: req.query
|
|
664
|
+
})
|
|
665
|
+
);
|
|
666
|
+
var getCredentialCount = async (req, res, opts) => handle(
|
|
667
|
+
res,
|
|
668
|
+
await getCredentialCountHandler({
|
|
669
|
+
authServerUrl: opts.authServerUrl,
|
|
670
|
+
authorization: buildServiceAuthorization(req, opts)
|
|
671
|
+
})
|
|
672
|
+
);
|
|
673
|
+
var listAllSessions = async (req, res, opts) => handle(
|
|
674
|
+
res,
|
|
675
|
+
await listAllSessionsHandler({
|
|
676
|
+
authServerUrl: opts.authServerUrl,
|
|
677
|
+
authorization: buildServiceAuthorization(req, opts),
|
|
678
|
+
query: req.query
|
|
679
|
+
})
|
|
680
|
+
);
|
|
681
|
+
var listUserSessions = async (req, res, opts) => handle(
|
|
682
|
+
res,
|
|
683
|
+
await listUserSessionsHandler(req.params.userId, {
|
|
684
|
+
authServerUrl: opts.authServerUrl,
|
|
685
|
+
authorization: buildServiceAuthorization(req, opts)
|
|
686
|
+
})
|
|
687
|
+
);
|
|
688
|
+
var revokeAllUserSessions = async (req, res, opts) => handle(
|
|
689
|
+
res,
|
|
690
|
+
await revokeAllUserSessionsHandler(req.params.userId, {
|
|
691
|
+
authServerUrl: opts.authServerUrl,
|
|
692
|
+
authorization: buildServiceAuthorization(req, opts)
|
|
693
|
+
})
|
|
694
|
+
);
|
|
695
|
+
|
|
349
696
|
// src/createServer.ts
|
|
350
697
|
import {
|
|
351
698
|
authFetch
|
|
352
699
|
} from "@seamless-auth/core";
|
|
700
|
+
|
|
701
|
+
// src/handlers/bootstrapAdmininvite.ts
|
|
702
|
+
import { bootstrapAdminInviteHandler } from "@seamless-auth/core/handlers/bootstrapAdminInvite";
|
|
703
|
+
async function bootstrapAdminInvite(req, res, opts) {
|
|
704
|
+
const result = await bootstrapAdminInviteHandler({
|
|
705
|
+
authServerUrl: opts.authServerUrl,
|
|
706
|
+
email: req.body.email,
|
|
707
|
+
authorization: req.headers["authorization"],
|
|
708
|
+
externalDelivery: Boolean(opts.messaging)
|
|
709
|
+
});
|
|
710
|
+
if (result.error) {
|
|
711
|
+
return res.status(result.status).json({ error: result.error });
|
|
712
|
+
}
|
|
713
|
+
if (result.body && typeof result.body === "object" && "delivery" in result.body) {
|
|
714
|
+
await deliverAuthMessage(
|
|
715
|
+
opts.messaging,
|
|
716
|
+
result.body.delivery
|
|
717
|
+
);
|
|
718
|
+
return res.status(result.status).json(stripDelivery(result.body));
|
|
719
|
+
}
|
|
720
|
+
res.status(result.status).json(result.body);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// src/handlers/systemConfig.ts
|
|
724
|
+
import {
|
|
725
|
+
getAvailableRolesHandler,
|
|
726
|
+
getSystemConfigAdminHandler,
|
|
727
|
+
updateSystemConfigHandler
|
|
728
|
+
} from "@seamless-auth/core/handlers/systemConfig";
|
|
729
|
+
async function getAvailableRoles(req, res, opts) {
|
|
730
|
+
const authorization = buildServiceAuthorization(req, opts);
|
|
731
|
+
const result = await getAvailableRolesHandler({
|
|
732
|
+
authServerUrl: opts.authServerUrl,
|
|
733
|
+
authorization
|
|
734
|
+
});
|
|
735
|
+
if (result.error) {
|
|
736
|
+
return res.status(result.status).json({ error: result.error });
|
|
737
|
+
}
|
|
738
|
+
res.status(result.status).json(result.body);
|
|
739
|
+
}
|
|
740
|
+
async function getSystemConfigAdmin(req, res, opts) {
|
|
741
|
+
const authorization = buildServiceAuthorization(req, opts);
|
|
742
|
+
const result = await getSystemConfigAdminHandler({
|
|
743
|
+
authServerUrl: opts.authServerUrl,
|
|
744
|
+
authorization
|
|
745
|
+
});
|
|
746
|
+
if (result.error) {
|
|
747
|
+
return res.status(result.status).json({ error: result.error });
|
|
748
|
+
}
|
|
749
|
+
res.status(result.status).json(result.body);
|
|
750
|
+
}
|
|
751
|
+
async function updateSystemConfig(req, res, opts) {
|
|
752
|
+
const authorization = buildServiceAuthorization(req, opts);
|
|
753
|
+
const result = await updateSystemConfigHandler({
|
|
754
|
+
authServerUrl: opts.authServerUrl,
|
|
755
|
+
authorization,
|
|
756
|
+
payload: req.body
|
|
757
|
+
});
|
|
758
|
+
if (result.error) {
|
|
759
|
+
return res.status(result.status).json({ error: result.error });
|
|
760
|
+
}
|
|
761
|
+
res.status(result.status).json(result.body);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// src/handlers/internalMetrics.ts
|
|
765
|
+
import {
|
|
766
|
+
getAuthEventSummaryHandler,
|
|
767
|
+
getAuthEventTimeseriesHandler,
|
|
768
|
+
getLoginStatsHandler,
|
|
769
|
+
getSecurityAnomaliesHandler,
|
|
770
|
+
getDashboardMetricsHandler,
|
|
771
|
+
getGroupedEventSummaryHandler
|
|
772
|
+
} from "@seamless-auth/core/handlers/internalMetrics";
|
|
773
|
+
function handle2(res, result) {
|
|
774
|
+
if (result.error) {
|
|
775
|
+
return res.status(result.status).json({ error: result.error });
|
|
776
|
+
}
|
|
777
|
+
return res.status(result.status).json(result.body);
|
|
778
|
+
}
|
|
779
|
+
async function getAuthEventSummary(req, res, opts) {
|
|
780
|
+
const authorization = buildServiceAuthorization(req, opts);
|
|
781
|
+
const result = await getAuthEventSummaryHandler({
|
|
782
|
+
authServerUrl: opts.authServerUrl,
|
|
783
|
+
authorization,
|
|
784
|
+
query: req.query
|
|
785
|
+
});
|
|
786
|
+
return handle2(res, result);
|
|
787
|
+
}
|
|
788
|
+
async function getAuthEventTimeseries(req, res, opts) {
|
|
789
|
+
const authorization = buildServiceAuthorization(req, opts);
|
|
790
|
+
const result = await getAuthEventTimeseriesHandler({
|
|
791
|
+
authServerUrl: opts.authServerUrl,
|
|
792
|
+
authorization,
|
|
793
|
+
query: req.query
|
|
794
|
+
});
|
|
795
|
+
return handle2(res, result);
|
|
796
|
+
}
|
|
797
|
+
async function getLoginStats(req, res, opts) {
|
|
798
|
+
const authorization = buildServiceAuthorization(req, opts);
|
|
799
|
+
const result = await getLoginStatsHandler({
|
|
800
|
+
authServerUrl: opts.authServerUrl,
|
|
801
|
+
authorization
|
|
802
|
+
});
|
|
803
|
+
return handle2(res, result);
|
|
804
|
+
}
|
|
805
|
+
async function getSecurityAnomalies(req, res, opts) {
|
|
806
|
+
const authorization = buildServiceAuthorization(req, opts);
|
|
807
|
+
const result = await getSecurityAnomaliesHandler({
|
|
808
|
+
authServerUrl: opts.authServerUrl,
|
|
809
|
+
authorization
|
|
810
|
+
});
|
|
811
|
+
return handle2(res, result);
|
|
812
|
+
}
|
|
813
|
+
async function getDashboardMetrics(req, res, opts) {
|
|
814
|
+
const authorization = buildServiceAuthorization(req, opts);
|
|
815
|
+
const result = await getDashboardMetricsHandler({
|
|
816
|
+
authServerUrl: opts.authServerUrl,
|
|
817
|
+
authorization
|
|
818
|
+
});
|
|
819
|
+
return handle2(res, result);
|
|
820
|
+
}
|
|
821
|
+
async function getGroupedEventSummary(req, res, opts) {
|
|
822
|
+
const authorization = buildServiceAuthorization(req, opts);
|
|
823
|
+
const result = await getGroupedEventSummaryHandler({
|
|
824
|
+
authServerUrl: opts.authServerUrl,
|
|
825
|
+
authorization
|
|
826
|
+
});
|
|
827
|
+
return handle2(res, result);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// src/handlers/sessions.ts
|
|
831
|
+
import {
|
|
832
|
+
listSessionsHandler,
|
|
833
|
+
revokeSessionHandler,
|
|
834
|
+
revokeAllSessionsHandler
|
|
835
|
+
} from "@seamless-auth/core/handlers/sessions";
|
|
836
|
+
function handle3(res, result) {
|
|
837
|
+
if (result.error) {
|
|
838
|
+
return res.status(result.status).json({ error: result.error });
|
|
839
|
+
}
|
|
840
|
+
return res.status(result.status).json(result.body);
|
|
841
|
+
}
|
|
842
|
+
async function listSessions(req, res, opts) {
|
|
843
|
+
const authorization = buildServiceAuthorization(req, opts);
|
|
844
|
+
const result = await listSessionsHandler({
|
|
845
|
+
authServerUrl: opts.authServerUrl,
|
|
846
|
+
authorization
|
|
847
|
+
});
|
|
848
|
+
return handle3(res, result);
|
|
849
|
+
}
|
|
850
|
+
async function revokeSession(req, res, opts) {
|
|
851
|
+
const authorization = buildServiceAuthorization(req, opts);
|
|
852
|
+
const result = await revokeSessionHandler(req.params.id, {
|
|
853
|
+
authServerUrl: opts.authServerUrl,
|
|
854
|
+
authorization
|
|
855
|
+
});
|
|
856
|
+
return handle3(res, result);
|
|
857
|
+
}
|
|
858
|
+
async function revokeAllSessions(req, res, opts) {
|
|
859
|
+
const authorization = buildServiceAuthorization(req, opts);
|
|
860
|
+
const result = await revokeAllSessionsHandler({
|
|
861
|
+
authServerUrl: opts.authServerUrl,
|
|
862
|
+
authorization
|
|
863
|
+
});
|
|
864
|
+
return handle3(res, result);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// src/createServer.ts
|
|
353
868
|
function createSeamlessAuthServer(opts) {
|
|
354
869
|
const r = express.Router();
|
|
355
870
|
r.use(express.json());
|
|
@@ -365,7 +880,8 @@ function createSeamlessAuthServer(opts) {
|
|
|
365
880
|
accessCookieName: opts.accessCookieName ?? "seamless-access",
|
|
366
881
|
registrationCookieName: opts.registrationCookieName ?? "seamless-ephemeral",
|
|
367
882
|
refreshCookieName: opts.refreshCookieName ?? "seamless-refresh",
|
|
368
|
-
preAuthCookieName: opts.preAuthCookieName ?? "seamless-ephemeral"
|
|
883
|
+
preAuthCookieName: opts.preAuthCookieName ?? "seamless-ephemeral",
|
|
884
|
+
messaging: opts.messaging
|
|
369
885
|
};
|
|
370
886
|
const proxyWithIdentity = (path, identity, method = "POST") => async (req, res) => {
|
|
371
887
|
if (!req.cookiePayload?.sub) {
|
|
@@ -438,11 +954,11 @@ function createSeamlessAuthServer(opts) {
|
|
|
438
954
|
);
|
|
439
955
|
r.get(
|
|
440
956
|
"/otp/generate-phone-otp",
|
|
441
|
-
|
|
957
|
+
(req, res) => requestOtp(req, res, resolvedOpts, "phone")
|
|
442
958
|
);
|
|
443
959
|
r.get(
|
|
444
960
|
"/otp/generate-email-otp",
|
|
445
|
-
|
|
961
|
+
(req, res) => requestOtp(req, res, resolvedOpts, "email")
|
|
446
962
|
);
|
|
447
963
|
r.post("/login", (req, res) => login(req, res, resolvedOpts));
|
|
448
964
|
r.post(
|
|
@@ -460,7 +976,10 @@ function createSeamlessAuthServer(opts) {
|
|
|
460
976
|
"/users/credentials",
|
|
461
977
|
proxyWithIdentity("users/credentials", "access")
|
|
462
978
|
);
|
|
463
|
-
r.get(
|
|
979
|
+
r.get(
|
|
980
|
+
"/magic-link",
|
|
981
|
+
(req, res) => requestMagicLink(req, res, resolvedOpts)
|
|
982
|
+
);
|
|
464
983
|
r.get("/magic-link/verify/:token", async (req, res) => {
|
|
465
984
|
const upstream = await authFetch(
|
|
466
985
|
`${resolvedOpts.authServerUrl}/magic-link/verify/${req.params.token}`,
|
|
@@ -473,11 +992,101 @@ function createSeamlessAuthServer(opts) {
|
|
|
473
992
|
"/magic-link/check",
|
|
474
993
|
(req, res) => pollMagicLinkConfirmation(req, res, resolvedOpts)
|
|
475
994
|
);
|
|
995
|
+
r.post(
|
|
996
|
+
"/internal/bootstrap/admin-invite",
|
|
997
|
+
(req, res) => bootstrapAdminInvite(req, res, resolvedOpts)
|
|
998
|
+
);
|
|
999
|
+
r.get(
|
|
1000
|
+
"/system-config/roles",
|
|
1001
|
+
(req, res) => getAvailableRoles(req, res, resolvedOpts)
|
|
1002
|
+
);
|
|
1003
|
+
r.get(
|
|
1004
|
+
"/system-config/admin",
|
|
1005
|
+
(req, res) => getSystemConfigAdmin(req, res, resolvedOpts)
|
|
1006
|
+
);
|
|
1007
|
+
r.patch(
|
|
1008
|
+
"/system-config/admin",
|
|
1009
|
+
(req, res) => updateSystemConfig(req, res, resolvedOpts)
|
|
1010
|
+
);
|
|
1011
|
+
r.get(
|
|
1012
|
+
"/internal/auth-events/summary",
|
|
1013
|
+
(req, res) => getAuthEventSummary(req, res, resolvedOpts)
|
|
1014
|
+
);
|
|
1015
|
+
r.get(
|
|
1016
|
+
"/internal/auth-events/timeseries",
|
|
1017
|
+
(req, res) => getAuthEventTimeseries(req, res, resolvedOpts)
|
|
1018
|
+
);
|
|
1019
|
+
r.get(
|
|
1020
|
+
"/internal/auth-events/login-stats",
|
|
1021
|
+
(req, res) => getLoginStats(req, res, resolvedOpts)
|
|
1022
|
+
);
|
|
1023
|
+
r.get(
|
|
1024
|
+
"/internal/security/anomalies",
|
|
1025
|
+
(req, res) => getSecurityAnomalies(req, res, resolvedOpts)
|
|
1026
|
+
);
|
|
1027
|
+
r.get(
|
|
1028
|
+
"/internal/metrics/dashboard",
|
|
1029
|
+
(req, res) => getDashboardMetrics(req, res, resolvedOpts)
|
|
1030
|
+
);
|
|
1031
|
+
r.get(
|
|
1032
|
+
"/internal/auth-events/grouped",
|
|
1033
|
+
(req, res) => getGroupedEventSummary(req, res, resolvedOpts)
|
|
1034
|
+
);
|
|
1035
|
+
r.get("/admin/users", (req, res) => getUsers(req, res, resolvedOpts));
|
|
1036
|
+
r.post(
|
|
1037
|
+
"/admin/users",
|
|
1038
|
+
(req, res) => createUser(req, res, resolvedOpts)
|
|
1039
|
+
);
|
|
1040
|
+
r.delete(
|
|
1041
|
+
"/admin/users",
|
|
1042
|
+
(req, res) => deleteUser(req, res, resolvedOpts)
|
|
1043
|
+
);
|
|
1044
|
+
r.patch(
|
|
1045
|
+
"/admin/users/:userId",
|
|
1046
|
+
(req, res) => updateUser(req, res, resolvedOpts)
|
|
1047
|
+
);
|
|
1048
|
+
r.get(
|
|
1049
|
+
"/admin/users/:userId",
|
|
1050
|
+
(req, res) => getUserDetail(req, res, resolvedOpts)
|
|
1051
|
+
);
|
|
1052
|
+
r.get(
|
|
1053
|
+
"/admin/users/:userId/anomalies",
|
|
1054
|
+
(req, res) => getUserAnomalies(req, res, resolvedOpts)
|
|
1055
|
+
);
|
|
1056
|
+
r.get(
|
|
1057
|
+
"/admin/auth-events",
|
|
1058
|
+
(req, res) => getAuthEvents(req, res, resolvedOpts)
|
|
1059
|
+
);
|
|
1060
|
+
r.get(
|
|
1061
|
+
"/admin/credential-count",
|
|
1062
|
+
(req, res) => getCredentialCount(req, res, resolvedOpts)
|
|
1063
|
+
);
|
|
1064
|
+
r.get(
|
|
1065
|
+
"/admin/sessions",
|
|
1066
|
+
(req, res) => listAllSessions(req, res, resolvedOpts)
|
|
1067
|
+
);
|
|
1068
|
+
r.get(
|
|
1069
|
+
"/admin/sessions/:userId",
|
|
1070
|
+
(req, res) => listUserSessions(req, res, resolvedOpts)
|
|
1071
|
+
);
|
|
1072
|
+
r.delete(
|
|
1073
|
+
"/admin/sessions/:userId/revoke-all",
|
|
1074
|
+
(req, res) => revokeAllUserSessions(req, res, resolvedOpts)
|
|
1075
|
+
);
|
|
1076
|
+
r.get("/sessions", (req, res) => listSessions(req, res, resolvedOpts));
|
|
1077
|
+
r.delete(
|
|
1078
|
+
"/sessions/:id",
|
|
1079
|
+
(req, res) => revokeSession(req, res, resolvedOpts)
|
|
1080
|
+
);
|
|
1081
|
+
r.delete(
|
|
1082
|
+
"/sessions",
|
|
1083
|
+
(req, res) => revokeAllSessions(req, res, resolvedOpts)
|
|
1084
|
+
);
|
|
476
1085
|
return r;
|
|
477
1086
|
}
|
|
478
1087
|
|
|
479
1088
|
// src/middleware/requireAuth.ts
|
|
480
|
-
import { verifyCookieJwt } from "@seamless-auth/core";
|
|
1089
|
+
import { verifyCookieJwt as verifyCookieJwt2 } from "@seamless-auth/core";
|
|
481
1090
|
function requireAuth(opts) {
|
|
482
1091
|
const { cookieName = "seamless-access", cookieSecret } = opts;
|
|
483
1092
|
if (!cookieSecret) {
|
|
@@ -495,7 +1104,7 @@ function requireAuth(opts) {
|
|
|
495
1104
|
});
|
|
496
1105
|
return;
|
|
497
1106
|
}
|
|
498
|
-
const payload =
|
|
1107
|
+
const payload = verifyCookieJwt2(token, cookieSecret);
|
|
499
1108
|
if (!payload || !payload.sub) {
|
|
500
1109
|
res.status(401).json({
|
|
501
1110
|
error: "Invalid or expired session"
|