najm-auth 4.0.3 → 4.0.4
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 +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +38 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -139,7 +139,14 @@ auth({
|
|
|
139
139
|
bcryptRounds?: number // Default: 10 (valid: 4-31)
|
|
140
140
|
|
|
141
141
|
// Frontend
|
|
142
|
-
frontendUrl?: string // Password reset link base URL
|
|
142
|
+
frontendUrl?: string // Password reset link base URL
|
|
143
|
+
appName?: string // Security email brand (default: 'Your app')
|
|
144
|
+
accountInviteLogo?: { // Optional CID-backed inline mark
|
|
145
|
+
alt?: string
|
|
146
|
+
contentBase64: string
|
|
147
|
+
contentType: string
|
|
148
|
+
filename: string
|
|
149
|
+
}
|
|
143
150
|
|
|
144
151
|
// Login identifier normalization (see "Identity presets")
|
|
145
152
|
identity?: {
|
|
@@ -1142,6 +1149,17 @@ activates the account when its status is `pending`. An ordinary password reset
|
|
|
1142
1149
|
changes neither verification nor lifecycle status, and an explicitly inactive
|
|
1143
1150
|
invited account remains inactive.
|
|
1144
1151
|
|
|
1152
|
+
Set `appName` on `auth()` to brand the invitation subject and email card. The
|
|
1153
|
+
provisioned role is presented as the account type, so a sponsor invitation can
|
|
1154
|
+
say “Activate your sponsor account” without application-owned HTML. The shared
|
|
1155
|
+
template uses inline critical styles for Gmail and keeps the raw token URL out
|
|
1156
|
+
of visible fallback copy.
|
|
1157
|
+
|
|
1158
|
+
For a branded mark that works in email clients without a public asset URL, set
|
|
1159
|
+
`accountInviteLogo` to base64 content plus its MIME type and filename. Najm
|
|
1160
|
+
attaches it inline and points the shared template at a stable CID; when omitted,
|
|
1161
|
+
the template renders `appName` as text.
|
|
1162
|
+
|
|
1145
1163
|
The built-in memory and Redis drivers implement the required atomic primitive.
|
|
1146
1164
|
A custom cache driver may omit `compareAndDelete()` for compatibility with
|
|
1147
1165
|
unrelated cache usage, but reset and invite consumption then fails closed. Do
|
package/dist/index.d.ts
CHANGED
|
@@ -198,6 +198,15 @@ interface AuthConfig {
|
|
|
198
198
|
defaultRole: string | null;
|
|
199
199
|
/** Frontend URL for password reset links (default: 'http://localhost:3000') */
|
|
200
200
|
frontendUrl: string;
|
|
201
|
+
/** Product name used in security email subjects and templates. */
|
|
202
|
+
appName: string;
|
|
203
|
+
/** Optional inline logo embedded in account invitation emails. */
|
|
204
|
+
accountInviteLogo?: {
|
|
205
|
+
alt?: string;
|
|
206
|
+
contentBase64: string;
|
|
207
|
+
contentType: string;
|
|
208
|
+
filename: string;
|
|
209
|
+
};
|
|
201
210
|
/** Registration mode: 'active' auto-activates, 'pending' requires admin approval (default: 'active') */
|
|
202
211
|
registrationMode: 'active' | 'pending';
|
|
203
212
|
/** Whether the unauthenticated POST /auth/register route is mounted. */
|
|
@@ -265,6 +274,15 @@ type AuthPluginConfig = {
|
|
|
265
274
|
defaultRole?: string | null;
|
|
266
275
|
/** Frontend URL for password reset links. Falls back to FRONTEND_URL env var, then 'http://localhost:3000' */
|
|
267
276
|
frontendUrl?: string;
|
|
277
|
+
/** Product name used in account invitation emails (default: 'Your app'). */
|
|
278
|
+
appName?: string;
|
|
279
|
+
/** Optional inline logo embedded in account invitation emails. */
|
|
280
|
+
accountInviteLogo?: {
|
|
281
|
+
alt?: string;
|
|
282
|
+
contentBase64: string;
|
|
283
|
+
contentType: string;
|
|
284
|
+
filename: string;
|
|
285
|
+
};
|
|
268
286
|
/** Registration mode: 'active' auto-activates new users, 'pending' requires admin approval (default: 'active') */
|
|
269
287
|
registrationMode?: 'active' | 'pending';
|
|
270
288
|
/**
|
|
@@ -432,7 +450,7 @@ var auth = {
|
|
|
432
450
|
subject: "Reset your password"
|
|
433
451
|
},
|
|
434
452
|
accountInvite: {
|
|
435
|
-
subject: "
|
|
453
|
+
subject: "{{appName}}: activate your {{accountLabel}}"
|
|
436
454
|
}
|
|
437
455
|
}
|
|
438
456
|
};
|
package/dist/index.js
CHANGED
|
@@ -3488,13 +3488,36 @@ var AuthService = class AuthService2 {
|
|
|
3488
3488
|
});
|
|
3489
3489
|
const { token } = await this.tokenService.generateInviteToken(user.id);
|
|
3490
3490
|
const inviteLink = `${this.config.frontendUrl}/reset-password?token=${token}`;
|
|
3491
|
+
const accountType = body.role?.trim().toLowerCase();
|
|
3492
|
+
const accountLabel = accountType ? `${accountType} account` : "account";
|
|
3491
3493
|
let emailSent = false;
|
|
3492
3494
|
try {
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3495
|
+
const logo = this.config.accountInviteLogo;
|
|
3496
|
+
const logoCid = logo ? "najm-account-invite-logo" : void 0;
|
|
3497
|
+
const result = await this.emailService.send({
|
|
3498
|
+
to: body.email,
|
|
3499
|
+
subject: this.t("emails.accountInvite.subject", {
|
|
3500
|
+
accountLabel,
|
|
3501
|
+
appName: this.config.appName
|
|
3502
|
+
}),
|
|
3503
|
+
html: accountInviteTemplate({
|
|
3504
|
+
accountType,
|
|
3505
|
+
appName: this.config.appName,
|
|
3506
|
+
inviteLink,
|
|
3507
|
+
logoAlt: logo?.alt,
|
|
3508
|
+
logoSrc: logoCid ? `cid:${logoCid}` : void 0,
|
|
3509
|
+
userName: user.name || body.email
|
|
3510
|
+
}),
|
|
3511
|
+
attachments: logo ? [{
|
|
3512
|
+
filename: logo.filename,
|
|
3513
|
+
content: logo.contentBase64,
|
|
3514
|
+
contentType: logo.contentType,
|
|
3515
|
+
cid: logoCid,
|
|
3516
|
+
disposition: "inline",
|
|
3517
|
+
encoding: "base64"
|
|
3518
|
+
}] : void 0
|
|
3519
|
+
});
|
|
3520
|
+
emailSent = result.success;
|
|
3498
3521
|
} catch (error) {
|
|
3499
3522
|
this.logger.warn("Account invite email failed", { email: body.email, error });
|
|
3500
3523
|
}
|
|
@@ -6346,7 +6369,7 @@ var en_default = {
|
|
|
6346
6369
|
subject: "Reset your password"
|
|
6347
6370
|
},
|
|
6348
6371
|
accountInvite: {
|
|
6349
|
-
subject: "
|
|
6372
|
+
subject: "{{appName}}: activate your {{accountLabel}}"
|
|
6350
6373
|
}
|
|
6351
6374
|
}
|
|
6352
6375
|
},
|
|
@@ -7478,6 +7501,13 @@ var validateCallbackUrl = /* @__PURE__ */ __name((value, name) => {
|
|
|
7478
7501
|
}
|
|
7479
7502
|
return callback.toString();
|
|
7480
7503
|
}, "validateCallbackUrl");
|
|
7504
|
+
var resolveAppName = /* @__PURE__ */ __name((value) => {
|
|
7505
|
+
const appName = value?.trim() || "Your app";
|
|
7506
|
+
if (appName.length > 80 || /[\u0000-\u001f\u007f]/.test(appName)) {
|
|
7507
|
+
throw new Error("auth.appName must be at most 80 characters without control characters");
|
|
7508
|
+
}
|
|
7509
|
+
return appName;
|
|
7510
|
+
}, "resolveAppName");
|
|
7481
7511
|
var resolveGoogleConfig = /* @__PURE__ */ __name((config) => {
|
|
7482
7512
|
const configuredGoogle = config?.oauth?.google;
|
|
7483
7513
|
if (!configuredGoogle)
|
|
@@ -7554,6 +7584,8 @@ var resolveAuthConfig = /* @__PURE__ */ __name((config) => {
|
|
|
7554
7584
|
blacklistPrefix: config?.blacklistPrefix ?? "auth:blacklist:",
|
|
7555
7585
|
defaultRole: config?.defaultRole ?? null,
|
|
7556
7586
|
frontendUrl: config?.frontendUrl ?? process.env.FRONTEND_URL ?? "http://localhost:3000",
|
|
7587
|
+
appName: resolveAppName(config?.appName),
|
|
7588
|
+
accountInviteLogo: config?.accountInviteLogo,
|
|
7557
7589
|
registrationMode: config?.registrationMode ?? "active",
|
|
7558
7590
|
publicRegistration: config?.publicRegistration ?? true,
|
|
7559
7591
|
requireVerifiedEmail: config?.requireVerifiedEmail ?? false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "najm-auth",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.4",
|
|
4
4
|
"description": "Authentication and authorization library for najm framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"najm-guard": "^2.0.2",
|
|
98
98
|
"najm-i18n": "^2.1.2",
|
|
99
99
|
"najm-cache": "^2.2.0",
|
|
100
|
-
"najm-email": "^2.0.
|
|
100
|
+
"najm-email": "^2.0.4",
|
|
101
101
|
"najm-rate": "^2.1.1",
|
|
102
102
|
"najm-validation": "^2.0.2",
|
|
103
103
|
"jsonwebtoken": "^9.0.3",
|