tempest-express-sdk 0.6.0 → 0.7.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 +3 -3
- package/dist/chunk-PKUUVK7K.js +6 -0
- package/dist/{chunk-3IDD2UXU.js.map → chunk-PKUUVK7K.js.map} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +239 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +252 -2
- package/dist/index.d.ts +252 -2
- package/dist/index.js +232 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-3IDD2UXU.js +0 -6
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { VERSION } from './chunk-
|
|
1
|
+
export { VERSION } from './chunk-PKUUVK7K.js';
|
|
2
2
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
3
3
|
import { extendZodWithOpenApi, OpenAPIRegistry, OpenApiGeneratorV31, OpenApiGeneratorV3 } from '@asteasolutions/zod-to-openapi';
|
|
4
4
|
export { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
|
|
@@ -8512,6 +8512,17 @@ var authResponseSchema = z.object({
|
|
|
8512
8512
|
user: userPublicSchema,
|
|
8513
8513
|
tokens: tokenPairSchema
|
|
8514
8514
|
}).openapi("AuthResponse");
|
|
8515
|
+
var mfaEnrollResponseSchema = z.object({
|
|
8516
|
+
secret: z.string().openapi({ description: "Base32 TOTP secret (manual entry)." }),
|
|
8517
|
+
otpauthUri: z.string().openapi({ description: "otpauth:// URI to render as QR." })
|
|
8518
|
+
}).openapi("MfaEnrollResponse");
|
|
8519
|
+
var mfaCodeSchema = z.object({ code: z.string().min(1).openapi({ description: "Authenticator code." }) }).openapi("MfaCode");
|
|
8520
|
+
var activationSchema = z.object({ token: z.string().min(1).openapi({ description: "Activation token." }) }).openapi("Activation");
|
|
8521
|
+
var passwordResetRequestSchema = z.object({ email: z.string().email().openapi({ description: "Account email." }) }).openapi("PasswordResetRequest");
|
|
8522
|
+
var passwordResetConfirmSchema = z.object({
|
|
8523
|
+
token: z.string().min(1).openapi({ description: "Reset token." }),
|
|
8524
|
+
password: z.string().min(1).openapi({ description: "New plaintext password." })
|
|
8525
|
+
}).openapi("PasswordResetConfirm");
|
|
8515
8526
|
|
|
8516
8527
|
// src/auth/service.ts
|
|
8517
8528
|
function toPublic(user) {
|
|
@@ -8624,6 +8635,178 @@ var UserAuthService = class {
|
|
|
8624
8635
|
}
|
|
8625
8636
|
};
|
|
8626
8637
|
|
|
8638
|
+
// src/auth/mfa.ts
|
|
8639
|
+
var MfaService = class {
|
|
8640
|
+
store;
|
|
8641
|
+
totp;
|
|
8642
|
+
/**
|
|
8643
|
+
* @param options - Store and TOTP helper.
|
|
8644
|
+
*/
|
|
8645
|
+
constructor(options) {
|
|
8646
|
+
this.store = options.store;
|
|
8647
|
+
this.totp = options.totp;
|
|
8648
|
+
}
|
|
8649
|
+
/**
|
|
8650
|
+
* Begin enrollment: generate and persist a secret, return the QR URI.
|
|
8651
|
+
*
|
|
8652
|
+
* @param userId - The enrolling user.
|
|
8653
|
+
* @param accountName - Label shown in the authenticator (usually the email).
|
|
8654
|
+
* @returns The secret and provisioning URI.
|
|
8655
|
+
*/
|
|
8656
|
+
async enroll(userId, accountName) {
|
|
8657
|
+
const secret = this.totp.generateSecret();
|
|
8658
|
+
await this.store.setSecret(userId, secret);
|
|
8659
|
+
await this.store.setEnabled(userId, false);
|
|
8660
|
+
return { secret, otpauthUri: this.totp.provisioningUri(secret, accountName) };
|
|
8661
|
+
}
|
|
8662
|
+
/**
|
|
8663
|
+
* Confirm enrollment by verifying a code, enabling MFA on success.
|
|
8664
|
+
*
|
|
8665
|
+
* @param userId - The user.
|
|
8666
|
+
* @param code - The 6-digit code from the authenticator.
|
|
8667
|
+
* @throws {ValidationException} When no secret is pending or the code is wrong.
|
|
8668
|
+
*/
|
|
8669
|
+
async confirm(userId, code) {
|
|
8670
|
+
const secret = await this.store.getSecret(userId);
|
|
8671
|
+
if (!secret) throw new ValidationException({ message: "MFA not initialized" });
|
|
8672
|
+
if (!this.totp.verify(secret, code)) {
|
|
8673
|
+
throw new ValidationException({ message: "Invalid MFA code" });
|
|
8674
|
+
}
|
|
8675
|
+
await this.store.setEnabled(userId, true);
|
|
8676
|
+
}
|
|
8677
|
+
/**
|
|
8678
|
+
* Verify a code (login step). Returns `false` without throwing.
|
|
8679
|
+
*
|
|
8680
|
+
* @param userId - The user.
|
|
8681
|
+
* @param code - The submitted code.
|
|
8682
|
+
* @returns `true` when the code is valid.
|
|
8683
|
+
*/
|
|
8684
|
+
async verify(userId, code) {
|
|
8685
|
+
const secret = await this.store.getSecret(userId);
|
|
8686
|
+
return secret ? this.totp.verify(secret, code) : false;
|
|
8687
|
+
}
|
|
8688
|
+
/**
|
|
8689
|
+
* Disable MFA after verifying a code.
|
|
8690
|
+
*
|
|
8691
|
+
* @param userId - The user.
|
|
8692
|
+
* @param code - The submitted code.
|
|
8693
|
+
* @throws {ValidationException} When the code is invalid.
|
|
8694
|
+
*/
|
|
8695
|
+
async disable(userId, code) {
|
|
8696
|
+
if (!await this.verify(userId, code)) {
|
|
8697
|
+
throw new ValidationException({ message: "Invalid MFA code" });
|
|
8698
|
+
}
|
|
8699
|
+
await this.store.setEnabled(userId, false);
|
|
8700
|
+
await this.store.setSecret(userId, "");
|
|
8701
|
+
}
|
|
8702
|
+
};
|
|
8703
|
+
|
|
8704
|
+
// src/auth/activation.ts
|
|
8705
|
+
var ActivationService = class {
|
|
8706
|
+
store;
|
|
8707
|
+
ttlSeconds;
|
|
8708
|
+
/**
|
|
8709
|
+
* @param options - Store and token TTL.
|
|
8710
|
+
*/
|
|
8711
|
+
constructor(options) {
|
|
8712
|
+
this.store = options.store;
|
|
8713
|
+
this.ttlSeconds = options.ttlSeconds ?? 60 * 60 * 24;
|
|
8714
|
+
}
|
|
8715
|
+
/**
|
|
8716
|
+
* Start activation: issue a token and persist its hash.
|
|
8717
|
+
*
|
|
8718
|
+
* @param userId - The user to activate.
|
|
8719
|
+
* @returns The one-time plaintext token (embed in the activation link).
|
|
8720
|
+
*/
|
|
8721
|
+
async start(userId) {
|
|
8722
|
+
const { plaintext, tokenHash } = generateOpaqueToken();
|
|
8723
|
+
await this.store.saveActivationToken(
|
|
8724
|
+
userId,
|
|
8725
|
+
tokenHash,
|
|
8726
|
+
Date.now() + this.ttlSeconds * 1e3
|
|
8727
|
+
);
|
|
8728
|
+
return plaintext;
|
|
8729
|
+
}
|
|
8730
|
+
/**
|
|
8731
|
+
* Activate an account from a token.
|
|
8732
|
+
*
|
|
8733
|
+
* @param token - The plaintext token from the activation link.
|
|
8734
|
+
* @returns The activated user id.
|
|
8735
|
+
* @throws {InvalidTokenException} When the token is unknown or expired.
|
|
8736
|
+
*/
|
|
8737
|
+
async activate(token) {
|
|
8738
|
+
const tokenHash = hashOpaqueToken(token);
|
|
8739
|
+
const record = await this.store.findActivationToken(tokenHash);
|
|
8740
|
+
if (!record || record.expiresAt <= Date.now()) {
|
|
8741
|
+
throw new InvalidTokenException({ message: "Invalid or expired activation token" });
|
|
8742
|
+
}
|
|
8743
|
+
await this.store.activate(record.userId);
|
|
8744
|
+
await this.store.clearActivationToken(tokenHash);
|
|
8745
|
+
return record.userId;
|
|
8746
|
+
}
|
|
8747
|
+
};
|
|
8748
|
+
|
|
8749
|
+
// src/auth/passwordReset.ts
|
|
8750
|
+
var PasswordResetService = class {
|
|
8751
|
+
store;
|
|
8752
|
+
password;
|
|
8753
|
+
ttlSeconds;
|
|
8754
|
+
passwordMinLength;
|
|
8755
|
+
/**
|
|
8756
|
+
* @param options - Store, password hasher and policy.
|
|
8757
|
+
*/
|
|
8758
|
+
constructor(options) {
|
|
8759
|
+
this.store = options.store;
|
|
8760
|
+
this.password = options.password;
|
|
8761
|
+
this.ttlSeconds = options.ttlSeconds ?? 3600;
|
|
8762
|
+
this.passwordMinLength = options.passwordMinLength ?? 12;
|
|
8763
|
+
}
|
|
8764
|
+
/**
|
|
8765
|
+
* Request a reset for `email`.
|
|
8766
|
+
*
|
|
8767
|
+
* Returns the plaintext token only when the email maps to a user; otherwise
|
|
8768
|
+
* `null`. Callers should respond with the same success shape either way to
|
|
8769
|
+
* avoid user enumeration — email the token only when present.
|
|
8770
|
+
*
|
|
8771
|
+
* @param email - The account email.
|
|
8772
|
+
* @returns The one-time token, or `null` when no user matches.
|
|
8773
|
+
*/
|
|
8774
|
+
async request(email) {
|
|
8775
|
+
const userId = await this.store.findUserIdByEmail(email.toLowerCase());
|
|
8776
|
+
if (!userId) return null;
|
|
8777
|
+
const { plaintext, tokenHash } = generateOpaqueToken();
|
|
8778
|
+
await this.store.saveResetToken(
|
|
8779
|
+
userId,
|
|
8780
|
+
tokenHash,
|
|
8781
|
+
Date.now() + this.ttlSeconds * 1e3
|
|
8782
|
+
);
|
|
8783
|
+
return plaintext;
|
|
8784
|
+
}
|
|
8785
|
+
/**
|
|
8786
|
+
* Confirm a reset: validate the token + new password and rehash.
|
|
8787
|
+
*
|
|
8788
|
+
* @param token - The plaintext reset token.
|
|
8789
|
+
* @param newPassword - The new plaintext password.
|
|
8790
|
+
* @throws {ValidationException} When the new password is too short.
|
|
8791
|
+
* @throws {InvalidTokenException} When the token is unknown or expired.
|
|
8792
|
+
*/
|
|
8793
|
+
async confirm(token, newPassword) {
|
|
8794
|
+
if (newPassword.length < this.passwordMinLength) {
|
|
8795
|
+
throw new ValidationException({
|
|
8796
|
+
message: `Password must be at least ${this.passwordMinLength} characters`,
|
|
8797
|
+
details: { minLength: this.passwordMinLength }
|
|
8798
|
+
});
|
|
8799
|
+
}
|
|
8800
|
+
const tokenHash = hashOpaqueToken(token);
|
|
8801
|
+
const record = await this.store.findResetToken(tokenHash);
|
|
8802
|
+
if (!record || record.expiresAt <= Date.now()) {
|
|
8803
|
+
throw new InvalidTokenException({ message: "Invalid or expired reset token" });
|
|
8804
|
+
}
|
|
8805
|
+
await this.store.updatePassword(record.userId, await this.password.hash(newPassword));
|
|
8806
|
+
await this.store.clearResetToken(tokenHash);
|
|
8807
|
+
}
|
|
8808
|
+
};
|
|
8809
|
+
|
|
8627
8810
|
// src/auth/middleware.ts
|
|
8628
8811
|
function bearerToken(req) {
|
|
8629
8812
|
const header = req.header("authorization");
|
|
@@ -8726,6 +8909,53 @@ function makeAuthRouter(options) {
|
|
|
8726
8909
|
}
|
|
8727
8910
|
res.json(claims);
|
|
8728
8911
|
});
|
|
8912
|
+
if (options.activation) {
|
|
8913
|
+
const activation = options.activation;
|
|
8914
|
+
router.post(`${prefix}/activate`, async (req, res) => {
|
|
8915
|
+
const { token } = activationSchema.parse(req.body);
|
|
8916
|
+
const userId = await activation.activate(token);
|
|
8917
|
+
res.json({ activated: true, userId });
|
|
8918
|
+
});
|
|
8919
|
+
}
|
|
8920
|
+
if (options.passwordReset) {
|
|
8921
|
+
const reset = options.passwordReset;
|
|
8922
|
+
router.post(`${prefix}/password-reset/request`, async (req, res) => {
|
|
8923
|
+
const { email } = passwordResetRequestSchema.parse(req.body);
|
|
8924
|
+
const token = await reset.request(email);
|
|
8925
|
+
res.status(202).json({ requested: true, ...token ? { token } : {} });
|
|
8926
|
+
});
|
|
8927
|
+
router.post(`${prefix}/password-reset/confirm`, async (req, res) => {
|
|
8928
|
+
const { token, password } = passwordResetConfirmSchema.parse(req.body);
|
|
8929
|
+
await reset.confirm(token, password);
|
|
8930
|
+
res.json({ reset: true });
|
|
8931
|
+
});
|
|
8932
|
+
}
|
|
8933
|
+
if (options.mfa) {
|
|
8934
|
+
const mfa = options.mfa;
|
|
8935
|
+
const requireUser = (req) => {
|
|
8936
|
+
const claims = getAuth(req);
|
|
8937
|
+
if (!claims || typeof claims.sub !== "string") {
|
|
8938
|
+
throw new UnauthorizedException({ message: "Not authenticated" });
|
|
8939
|
+
}
|
|
8940
|
+
return claims.sub;
|
|
8941
|
+
};
|
|
8942
|
+
router.post(`${prefix}/mfa/enroll`, makeJwtAuthMiddleware(jwt), async (req, res) => {
|
|
8943
|
+
const claims = getAuth(req);
|
|
8944
|
+
const userId = requireUser(req);
|
|
8945
|
+
const label = typeof claims?.email === "string" ? claims.email : userId;
|
|
8946
|
+
res.json(await mfa.enroll(userId, label));
|
|
8947
|
+
});
|
|
8948
|
+
router.post(`${prefix}/mfa/confirm`, makeJwtAuthMiddleware(jwt), async (req, res) => {
|
|
8949
|
+
const { code } = mfaCodeSchema.parse(req.body);
|
|
8950
|
+
await mfa.confirm(requireUser(req), code);
|
|
8951
|
+
res.json({ enabled: true });
|
|
8952
|
+
});
|
|
8953
|
+
router.post(`${prefix}/mfa/disable`, makeJwtAuthMiddleware(jwt), async (req, res) => {
|
|
8954
|
+
const { code } = mfaCodeSchema.parse(req.body);
|
|
8955
|
+
await mfa.disable(requireUser(req), code);
|
|
8956
|
+
res.json({ enabled: false });
|
|
8957
|
+
});
|
|
8958
|
+
}
|
|
8729
8959
|
return router;
|
|
8730
8960
|
}
|
|
8731
8961
|
var logger2 = new JSONLogger("tempest_express_sdk.api.handlers");
|
|
@@ -8993,6 +9223,6 @@ function runServer(app, options = {}) {
|
|
|
8993
9223
|
});
|
|
8994
9224
|
}
|
|
8995
9225
|
|
|
8996
|
-
export { AdminSite, AppException, AttemptThrottle, BaseController, BaseModel, BaseService, CEP_PATTERN, CNPJ_PATTERN, CPF_PATTERN, CircuitOpenError, CompositeFeatureFlagBackend, ConflictException, DEFAULT_LOCALE, EmailUtils, EnvFeatureFlagBackend, EventStream, ExpiredTokenException, FeatureFlags, ForbiddenException, HTTPClient, HTTP_500_MARKER, InvalidTokenException, JSONLogger, JWTUtils, LocalUploadStorage, MemoryBroker, MemoryCacheManager, MemoryFeatureFlagBackend, MemorySessionStore, MemoryThrottleBackend, MessageCatalog, MetricsUtils, NotFoundException, PHONE_BR_PATTERN, PasswordUtils, REQUEST_ID_HEADER, RabbitBroker, RedisCacheManager, Region, RetryPolicy, SSEBroker, ServerSentEvent, SessionService, TOTPHelper, TaskManager, TelegramProvider, TooManyRequestsException, TwilioSmsProvider, UF, UnauthorizedException, UserAuthService, ValidationException, WebPushDispatcher, WebPushError, WebPushGoneError, WebSocketHub, WhatsAppProvider, attachWebSocketHub, authResponseSchema, baseAppSettingsSchema, baseAppSettingsShape, baseResponseSchema, bearerToken, buildContentDisposition, cached3 as cached, cepField, citiesByUf, cnpjField, coerceFlag, configureLogging, corsSettingsShape, cpfField, cpfOrCnpjField, createApp, createOpenApiRegistry, createdByColumn, cursorPaginationFilterSchema, cursorPaginationSchema, databaseSettingsShape, decodeCursor, defaultMessageCatalog, defineEnum, deletedAtColumn, encodeCursor, generateOpaqueToken, generateOpenApiDocument, getAuth, getClientIp, getConditions, getPaginationConditions, getRequestId, getState, hashOpaqueToken, inboundMessageSchema, isValidCep, isValidCity, isValidCnpj, isValidCpf, isValidCpfCnpj, isValidPhoneBr, isValidUf, listStates, loadSettings, loginSchema, makeAdminRouter, makeAppExceptionHandler, makeAuthRouter, makeFlagGuard, makeHealthRouter, makeJwtAuthMiddleware, makeSessionMiddleware, makeTwilioWebhookRouter, makeUnhandledExceptionHandler, makeWhatsAppWebhookRouter, modifyDict, mountOpenApiJson, mountRedoc, mountSwaggerUi, normalizeCep, normalizeCnpj, normalizeCpf, normalizeCpfCnpj, normalizePhoneBr, normalizeUf, notFoundHandler, onlyDigits, paginationFilterSchema, paginationSchema, parseAcceptLanguage, parseCookies, phoneBrField, refreshSchema, registerExceptionHandlers, requestIdMiddleware, requireRoles, runServer, runWithRequestContext, serverSettingsShape, sessionCookie, setRequestId, signupSchema, sseResponse, statesByRegion, tableNameFor, toDict, toUtc, tokenFromUrl, tokenPairSchema, ufField, updatedByColumn, userPublicSchema, utcnow, validateTwilioSignature, verifyOpaqueToken, webPushKeysSchema, webPushPayloadSchema, webPushSubscriptionSchema, wsEnvelopeSchema };
|
|
9226
|
+
export { ActivationService, AdminSite, AppException, AttemptThrottle, BaseController, BaseModel, BaseService, CEP_PATTERN, CNPJ_PATTERN, CPF_PATTERN, CircuitOpenError, CompositeFeatureFlagBackend, ConflictException, DEFAULT_LOCALE, EmailUtils, EnvFeatureFlagBackend, EventStream, ExpiredTokenException, FeatureFlags, ForbiddenException, HTTPClient, HTTP_500_MARKER, InvalidTokenException, JSONLogger, JWTUtils, LocalUploadStorage, MemoryBroker, MemoryCacheManager, MemoryFeatureFlagBackend, MemorySessionStore, MemoryThrottleBackend, MessageCatalog, MetricsUtils, MfaService, NotFoundException, PHONE_BR_PATTERN, PasswordResetService, PasswordUtils, REQUEST_ID_HEADER, RabbitBroker, RedisCacheManager, Region, RetryPolicy, SSEBroker, ServerSentEvent, SessionService, TOTPHelper, TaskManager, TelegramProvider, TooManyRequestsException, TwilioSmsProvider, UF, UnauthorizedException, UserAuthService, ValidationException, WebPushDispatcher, WebPushError, WebPushGoneError, WebSocketHub, WhatsAppProvider, activationSchema, attachWebSocketHub, authResponseSchema, baseAppSettingsSchema, baseAppSettingsShape, baseResponseSchema, bearerToken, buildContentDisposition, cached3 as cached, cepField, citiesByUf, cnpjField, coerceFlag, configureLogging, corsSettingsShape, cpfField, cpfOrCnpjField, createApp, createOpenApiRegistry, createdByColumn, cursorPaginationFilterSchema, cursorPaginationSchema, databaseSettingsShape, decodeCursor, defaultMessageCatalog, defineEnum, deletedAtColumn, encodeCursor, generateOpaqueToken, generateOpenApiDocument, getAuth, getClientIp, getConditions, getPaginationConditions, getRequestId, getState, hashOpaqueToken, inboundMessageSchema, isValidCep, isValidCity, isValidCnpj, isValidCpf, isValidCpfCnpj, isValidPhoneBr, isValidUf, listStates, loadSettings, loginSchema, makeAdminRouter, makeAppExceptionHandler, makeAuthRouter, makeFlagGuard, makeHealthRouter, makeJwtAuthMiddleware, makeSessionMiddleware, makeTwilioWebhookRouter, makeUnhandledExceptionHandler, makeWhatsAppWebhookRouter, mfaCodeSchema, mfaEnrollResponseSchema, modifyDict, mountOpenApiJson, mountRedoc, mountSwaggerUi, normalizeCep, normalizeCnpj, normalizeCpf, normalizeCpfCnpj, normalizePhoneBr, normalizeUf, notFoundHandler, onlyDigits, paginationFilterSchema, paginationSchema, parseAcceptLanguage, parseCookies, passwordResetConfirmSchema, passwordResetRequestSchema, phoneBrField, refreshSchema, registerExceptionHandlers, requestIdMiddleware, requireRoles, runServer, runWithRequestContext, serverSettingsShape, sessionCookie, setRequestId, signupSchema, sseResponse, statesByRegion, tableNameFor, toDict, toUtc, tokenFromUrl, tokenPairSchema, ufField, updatedByColumn, userPublicSchema, utcnow, validateTwilioSignature, verifyOpaqueToken, webPushKeysSchema, webPushPayloadSchema, webPushSubscriptionSchema, wsEnvelopeSchema };
|
|
8997
9227
|
//# sourceMappingURL=index.js.map
|
|
8998
9228
|
//# sourceMappingURL=index.js.map
|