@astram/guard 0.11.0 → 0.11.2
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/account.controller.d.ts +4 -1
- package/dist/auth/account.controller.d.ts.map +1 -1
- package/dist/auth/account.controller.js +40 -11
- package/dist/auth/auth.module.d.ts +4 -2
- package/dist/auth/auth.module.d.ts.map +1 -1
- package/dist/auth/auth.module.js +32 -11
- package/dist/auth/email-change-messages.d.ts +7 -2
- package/dist/auth/email-change-messages.d.ts.map +1 -1
- package/dist/auth/email-change-messages.js +8 -2
- package/dist/auth/link-base-url.d.ts +24 -0
- package/dist/auth/link-base-url.d.ts.map +1 -0
- package/dist/auth/link-base-url.js +93 -0
- package/dist/auth/ports/email-address-changer.d.ts +3 -1
- package/dist/auth/ports/email-address-changer.d.ts.map +1 -1
- package/dist/auth/ports/profile-writer.d.ts +5 -1
- package/dist/auth/ports/profile-writer.d.ts.map +1 -1
- package/dist/auth/tokens.d.ts +6 -3
- package/dist/auth/tokens.d.ts.map +1 -1
- package/dist/auth/tokens.js +6 -3
- package/dist/authentication/mariadb-email-address-changer.d.ts +10 -3
- package/dist/authentication/mariadb-email-address-changer.d.ts.map +1 -1
- package/dist/authentication/mariadb-email-address-changer.js +27 -9
- package/dist/authentication/mariadb-profile-writer.d.ts +2 -1
- package/dist/authentication/mariadb-profile-writer.d.ts.map +1 -1
- package/dist/authentication/mariadb-profile-writer.js +6 -4
- package/dist/module/shield.module.d.ts +4 -2
- package/dist/module/shield.module.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -173,7 +173,10 @@ export declare class AccountController {
|
|
|
173
173
|
* • a confirmed MFA device and no `code` → 400 `mfa_required`; wrong code → 400
|
|
174
174
|
* `invalid_request` (`mfa:pid` budget, replay-guarded TOTP or a recovery code, A1); locked → 429
|
|
175
175
|
* • `email-change:pid` budget exhausted → 429 `too_many_requests`
|
|
176
|
-
* • a limiter / token store / mail outage
|
|
176
|
+
* • a limiter / MFA device or recovery-code store / token store / mail outage → 503
|
|
177
|
+
* `authentication_unavailable` (the token is issued before the second factor is checked, so
|
|
178
|
+
* a STORE outage never spends a recovery code or a TOTP; a failed proof burns that token. A
|
|
179
|
+
* mail failure after a successful proof does spend it — the proof precedes the send)
|
|
177
180
|
* • name only → 200 (the refreshed CurrentUserProfile)
|
|
178
181
|
* • email accepted → 202 `{ pendingEmail: true, profile }`
|
|
179
182
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account.controller.d.ts","sourceRoot":"","sources":["../../src/auth/account.controller.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"account.controller.d.ts","sourceRoot":"","sources":["../../src/auth/account.controller.ts"],"names":[],"mappings":"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"}
|
|
@@ -406,7 +406,10 @@ let AccountController = class AccountController {
|
|
|
406
406
|
* • a confirmed MFA device and no `code` → 400 `mfa_required`; wrong code → 400
|
|
407
407
|
* `invalid_request` (`mfa:pid` budget, replay-guarded TOTP or a recovery code, A1); locked → 429
|
|
408
408
|
* • `email-change:pid` budget exhausted → 429 `too_many_requests`
|
|
409
|
-
* • a limiter / token store / mail outage
|
|
409
|
+
* • a limiter / MFA device or recovery-code store / token store / mail outage → 503
|
|
410
|
+
* `authentication_unavailable` (the token is issued before the second factor is checked, so
|
|
411
|
+
* a STORE outage never spends a recovery code or a TOTP; a failed proof burns that token. A
|
|
412
|
+
* mail failure after a successful proof does spend it — the proof precedes the send)
|
|
410
413
|
* • name only → 200 (the refreshed CurrentUserProfile)
|
|
411
414
|
* • email accepted → 202 `{ pendingEmail: true, profile }`
|
|
412
415
|
*
|
|
@@ -494,25 +497,35 @@ let AccountController = class AccountController {
|
|
|
494
497
|
invalidCurrentPassword();
|
|
495
498
|
await this.loginLimiter?.reset(loginKey);
|
|
496
499
|
// The per-account request budget, counted only once the password proved the caller (a typo
|
|
497
|
-
// never spends it); given back when the second factor is missing or wrong
|
|
500
|
+
// never spends it); given back when the second factor is missing or wrong, and when a store
|
|
501
|
+
// outage stops the request before any link is mailed (Phase 13 #2b). A mail failure keeps it
|
|
502
|
+
// counted: the sender may have delivered before it failed.
|
|
498
503
|
const requestKey = `email-change:pid:${claims.id}`;
|
|
499
504
|
await consumeAttempt(this.emailChangeLimiter, requestKey);
|
|
500
|
-
|
|
501
|
-
|
|
505
|
+
const giveBackRequest = async () => {
|
|
506
|
+
await this.emailChangeLimiter?.refund(requestKey).catch(() => undefined);
|
|
507
|
+
};
|
|
508
|
+
// A1: a confirmed MFA device ⇒ a second factor. A store outage here is a 503, not a 500.
|
|
509
|
+
let code = null;
|
|
502
510
|
try {
|
|
503
511
|
if ((await devices.countConfirmed(claims.id)) > 0) {
|
|
504
|
-
|
|
505
|
-
if (code === '')
|
|
506
|
-
mfaRequired();
|
|
507
|
-
await this.requireMfaProof(devices, claims.id, code);
|
|
512
|
+
code = typeof body?.code === 'string' ? body.code.trim() : '';
|
|
508
513
|
}
|
|
509
514
|
}
|
|
510
|
-
catch
|
|
511
|
-
await
|
|
512
|
-
|
|
515
|
+
catch {
|
|
516
|
+
await giveBackRequest();
|
|
517
|
+
retryableUnavailable();
|
|
518
|
+
}
|
|
519
|
+
if (code === '') {
|
|
520
|
+
await giveBackRequest();
|
|
521
|
+
mfaRequired();
|
|
513
522
|
}
|
|
514
523
|
// A2/A3/A4: one pending change; 32 random bytes, stored as sha256, bound to the canonical new
|
|
515
524
|
// address, the current email and this session; 30-minute TTL.
|
|
525
|
+
// Phase 13 #2b: the token is issued BEFORE the second factor is checked, so a token-store outage
|
|
526
|
+
// never spends a recovery code (consumed on a match) or a TOTP (claimed by the replay guard).
|
|
527
|
+
// Until the mail goes out the raw token exists only in this handler, so a row whose proof then
|
|
528
|
+
// fails is inert; it is burned anyway (best effort).
|
|
516
529
|
const rawToken = (0, node_crypto_1.randomBytes)(32).toString('hex');
|
|
517
530
|
try {
|
|
518
531
|
await tokens.issue({
|
|
@@ -526,8 +539,24 @@ let AccountController = class AccountController {
|
|
|
526
539
|
});
|
|
527
540
|
}
|
|
528
541
|
catch {
|
|
542
|
+
await giveBackRequest();
|
|
529
543
|
retryableUnavailable();
|
|
530
544
|
}
|
|
545
|
+
// A1: TOTP via the replay-guarded verifier, or a recovery code, on the shared `mfa:pid` budget,
|
|
546
|
+
// exactly like the code-gated MFA routes. Any refusal (400/429/503) burns the token just issued
|
|
547
|
+
// and gives the request budget back; a store error inside the proof is a 503, not a 500.
|
|
548
|
+
if (code !== null) {
|
|
549
|
+
try {
|
|
550
|
+
await this.requireMfaProof(devices, claims.id, code);
|
|
551
|
+
}
|
|
552
|
+
catch (err) {
|
|
553
|
+
await tokens.invalidateAllForPrincipal(claims.id).catch(() => undefined);
|
|
554
|
+
await giveBackRequest();
|
|
555
|
+
if (err instanceof common_1.HttpException)
|
|
556
|
+
throw err;
|
|
557
|
+
retryableUnavailable();
|
|
558
|
+
}
|
|
559
|
+
}
|
|
531
560
|
const link = (0, email_change_messages_js_1.emailChangeLink)(this.emailChangeLinkBaseUrl ?? email_change_messages_js_1.DEFAULT_EMAIL_CHANGE_LINK_BASE_URL, rawToken);
|
|
532
561
|
try {
|
|
533
562
|
await sender.send((0, email_change_messages_js_1.confirmEmailChangeMessage)(email, link));
|
|
@@ -220,8 +220,10 @@ export interface AuthModuleOptions {
|
|
|
220
220
|
readonly membershipChecker?: MembershipChecker;
|
|
221
221
|
/**
|
|
222
222
|
* The base URL of the app's confirm-email page (Phase 12, KI #14). The mailed link is
|
|
223
|
-
* `${emailChangeLinkBaseUrl}#t=<token>` (the token in the fragment, never the query).
|
|
224
|
-
* `http://localhost:5173/confirm-email`.
|
|
223
|
+
* `${emailChangeLinkBaseUrl}#t=<token>` (the token in the fragment, never the query). Option >
|
|
224
|
+
* env `EMAIL_CHANGE_LINK_BASE_URL` > dev default `http://localhost:5173/confirm-email`. Outside
|
|
225
|
+
* `NODE_ENV` development/test an unset or loopback base fails the boot unless
|
|
226
|
+
* `emailSender: 'disabled'` (Phase 13 #1; `RESET_LINK_BASE_URL` follows the same rule).
|
|
225
227
|
*/
|
|
226
228
|
readonly emailChangeLinkBaseUrl?: string;
|
|
227
229
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.module.d.ts","sourceRoot":"","sources":["../../src/auth/auth.module.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"auth.module.d.ts","sourceRoot":"","sources":["../../src/auth/auth.module.ts"],"names":[],"mappings":"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"}
|
package/dist/auth/auth.module.js
CHANGED
|
@@ -120,7 +120,7 @@ const mariadb_profile_writer_js_1 = require("../authentication/mariadb-profile-w
|
|
|
120
120
|
const mariadb_reset_token_store_js_1 = require("../authentication/mariadb-reset-token-store.js");
|
|
121
121
|
const mariadb_email_change_token_store_js_1 = require("../authentication/mariadb-email-change-token-store.js");
|
|
122
122
|
const mariadb_email_address_changer_js_1 = require("../authentication/mariadb-email-address-changer.js");
|
|
123
|
-
const
|
|
123
|
+
const link_base_url_js_1 = require("./link-base-url.js");
|
|
124
124
|
const mariadb_recovery_code_store_js_1 = require("../authentication/mariadb-recovery-code-store.js");
|
|
125
125
|
const mariadb_mfa_device_store_js_1 = require("../authentication/mariadb-mfa-device-store.js");
|
|
126
126
|
const mariadb_session_sweeper_js_1 = require("../authentication/mariadb-session-sweeper.js");
|
|
@@ -684,11 +684,20 @@ const confirmEmailLimiterProvider = {
|
|
|
684
684
|
useFactory: (redis) => new attempt_limiter_js_1.AttemptLimiter(redis, { maxAttempts: 10, windowMs: 15 * 60 * 1000 }),
|
|
685
685
|
inject: [tokens_js_1.SHIELD_REDIS],
|
|
686
686
|
};
|
|
687
|
-
/**
|
|
688
|
-
|
|
687
|
+
/**
|
|
688
|
+
* The confirm-link base (Phase 13 #1): `ShieldModule.forRoot({ emailChangeLinkBaseUrl })` >
|
|
689
|
+
* `EMAIL_CHANGE_LINK_BASE_URL` > dev default, read when the module compiles. Outside development/test
|
|
690
|
+
* (and unless `emailSender: 'disabled'`) an unset or loopback base FAILS the boot — see
|
|
691
|
+
* {@link resolveAuthLinkBaseUrls}.
|
|
692
|
+
*/
|
|
693
|
+
function makeEmailChangeLinkBaseUrlProvider(base, mailDisabled) {
|
|
689
694
|
return {
|
|
690
695
|
provide: tokens_js_1.SHIELD_EMAIL_CHANGE_LINK_BASE_URL,
|
|
691
|
-
|
|
696
|
+
useFactory: () => (0, link_base_url_js_1.resolveAuthLinkBaseUrls)({
|
|
697
|
+
env: process.env,
|
|
698
|
+
emailChangeLinkBaseUrlOption: base,
|
|
699
|
+
mailDisabled,
|
|
700
|
+
}).emailChangeLinkBaseUrl,
|
|
692
701
|
};
|
|
693
702
|
}
|
|
694
703
|
/**
|
|
@@ -807,12 +816,20 @@ function makeMembershipCheckerProvider(override) {
|
|
|
807
816
|
* (`${base}?token=<rawToken>`). A plain string VALUE (not a seam) resolved from the
|
|
808
817
|
* environment (`RESET_LINK_BASE_URL`) with a dev default, so the framework owns the
|
|
809
818
|
* URL composition and {@link SHIELD_EMAIL_SENDER} stays URL-agnostic. Always
|
|
810
|
-
* provided so the `@Optional()` injection resolves.
|
|
819
|
+
* provided so the `@Optional()` injection resolves. Read when the module compiles (not at import);
|
|
820
|
+
* outside development/test (and unless `emailSender: 'disabled'`) an unset or loopback base FAILS
|
|
821
|
+
* the boot (Phase 13 #1, {@link resolveAuthLinkBaseUrls}).
|
|
811
822
|
*/
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
823
|
+
function makeResetLinkBaseUrlProvider(emailChangeLinkBaseUrl, mailDisabled) {
|
|
824
|
+
return {
|
|
825
|
+
provide: tokens_js_1.SHIELD_RESET_LINK_BASE_URL,
|
|
826
|
+
useFactory: () => (0, link_base_url_js_1.resolveAuthLinkBaseUrls)({
|
|
827
|
+
env: process.env,
|
|
828
|
+
emailChangeLinkBaseUrlOption: emailChangeLinkBaseUrl,
|
|
829
|
+
mailDisabled,
|
|
830
|
+
}).resetLinkBaseUrl,
|
|
831
|
+
};
|
|
832
|
+
}
|
|
816
833
|
/**
|
|
817
834
|
* The auth composition module. Imports {@link PersistenceModule} (the live
|
|
818
835
|
* connections) and provides the adapters + composed engines. Exports the two
|
|
@@ -835,6 +852,10 @@ let AuthModule = AuthModule_1 = class AuthModule {
|
|
|
835
852
|
// through `ShieldModule.forRoot` — guard NEVER imports `@astram/identity`.
|
|
836
853
|
const identityActionKeys = options.identityActionKeys ?? [];
|
|
837
854
|
const identityControlPlaneKeys = options.identityControlPlaneKeys ?? [];
|
|
855
|
+
// The link-base boot rule (Phase 13 #1) applies only with a real sender: `'disabled'` delivers
|
|
856
|
+
// nothing, and an omitted sender outside development/test already fails the boot through
|
|
857
|
+
// `resolveDefaultEmailSender` (that clearer error is kept; in development/test the rule is off).
|
|
858
|
+
const mailDisabled = options.emailSender === undefined || options.emailSender === 'disabled';
|
|
838
859
|
return {
|
|
839
860
|
module: AuthModule_1,
|
|
840
861
|
imports: [persistence_module_js_1.PersistenceModule.forRoot()],
|
|
@@ -858,7 +879,7 @@ let AuthModule = AuthModule_1 = class AuthModule {
|
|
|
858
879
|
emailAddressChangerProvider,
|
|
859
880
|
emailChangeLimiterProvider,
|
|
860
881
|
confirmEmailLimiterProvider,
|
|
861
|
-
makeEmailChangeLinkBaseUrlProvider(options.emailChangeLinkBaseUrl),
|
|
882
|
+
makeEmailChangeLinkBaseUrlProvider(options.emailChangeLinkBaseUrl, mailDisabled),
|
|
862
883
|
makeEmailSenderProvider(options.emailSender),
|
|
863
884
|
makeRecoveryCodeStoreProvider(options.recoveryCodeStore),
|
|
864
885
|
makeProfileWriterProvider(options.profileWriter),
|
|
@@ -898,7 +919,7 @@ let AuthModule = AuthModule_1 = class AuthModule {
|
|
|
898
919
|
},
|
|
899
920
|
// The reset-link base URL value the forgot-password handler composes into the
|
|
900
921
|
// emailed link (framework-owned URL composition; EmailSender stays dumb).
|
|
901
|
-
|
|
922
|
+
makeResetLinkBaseUrlProvider(options.emailChangeLinkBaseUrl, mailDisabled),
|
|
902
923
|
// The resolved session lifetimes (#8) — one source for authority, binder, controller.
|
|
903
924
|
sessionLifetimesProvider,
|
|
904
925
|
sessionRecordStoreProvider,
|
|
@@ -12,8 +12,13 @@ import type { EmailMessage } from './ports/email-sender.js';
|
|
|
12
12
|
export declare const EMAIL_CHANGE_TTL_MS: number;
|
|
13
13
|
/** The default confirm-page base (a dev Vite origin), like the reset-link default. */
|
|
14
14
|
export declare const DEFAULT_EMAIL_CHANGE_LINK_BASE_URL = "http://localhost:5173/confirm-email";
|
|
15
|
-
/**
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* What to do if the change was not the owner's (A10, reworded in Phase 13 #2a). After an
|
|
17
|
+
* attacker's change the login hint may have moved with the email (it follows when it equalled the
|
|
18
|
+
* old address), so a reset from the login page may not reach the victim: the administrator comes
|
|
19
|
+
* first, and the reset advice is qualified rather than given as the remedy.
|
|
20
|
+
*/
|
|
21
|
+
export declare const NOT_YOU_SENTENCE: string;
|
|
17
22
|
/** The confirm link: the token rides in the fragment, never the query (A4). */
|
|
18
23
|
export declare function emailChangeLink(base: string, rawToken: string): string;
|
|
19
24
|
export declare function confirmEmailChangeMessage(to: string, link: string): EmailMessage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email-change-messages.d.ts","sourceRoot":"","sources":["../../src/auth/email-change-messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,8EAA8E;AAC9E,eAAO,MAAM,mBAAmB,QAAiB,CAAC;AAElD,sFAAsF;AACtF,eAAO,MAAM,kCAAkC,wCAAwC,CAAC;AAExF
|
|
1
|
+
{"version":3,"file":"email-change-messages.d.ts","sourceRoot":"","sources":["../../src/auth/email-change-messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,8EAA8E;AAC9E,eAAO,MAAM,mBAAmB,QAAiB,CAAC;AAElD,sFAAsF;AACtF,eAAO,MAAM,kCAAkC,wCAAwC,CAAC;AAExF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,QAE8C,CAAC;AAU5E,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CAehF;AAED,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,MAAM,EACV,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,MAAM,GACX,YAAY,CAYd"}
|
|
@@ -8,8 +8,14 @@ exports.emailChangedNoticeMessage = emailChangedNoticeMessage;
|
|
|
8
8
|
exports.EMAIL_CHANGE_TTL_MS = 30 * 60 * 1000;
|
|
9
9
|
/** The default confirm-page base (a dev Vite origin), like the reset-link default. */
|
|
10
10
|
exports.DEFAULT_EMAIL_CHANGE_LINK_BASE_URL = 'http://localhost:5173/confirm-email';
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* What to do if the change was not the owner's (A10, reworded in Phase 13 #2a). After an
|
|
13
|
+
* attacker's change the login hint may have moved with the email (it follows when it equalled the
|
|
14
|
+
* old address), so a reset from the login page may not reach the victim: the administrator comes
|
|
15
|
+
* first, and the reset advice is qualified rather than given as the remedy.
|
|
16
|
+
*/
|
|
17
|
+
exports.NOT_YOU_SENTENCE = "If this wasn't you, contact your administrator immediately. Your sign-in email may have " +
|
|
18
|
+
'changed too, so a password reset from the login page may not reach you.';
|
|
13
19
|
function escapeHtml(value) {
|
|
14
20
|
return value
|
|
15
21
|
.replace(/&/g, '&')
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** The default reset-page base (a dev Vite origin). */
|
|
2
|
+
export declare const DEFAULT_RESET_LINK_BASE_URL = "http://localhost:5173/reset-password";
|
|
3
|
+
export interface AuthLinkBaseUrls {
|
|
4
|
+
readonly resetLinkBaseUrl: string;
|
|
5
|
+
readonly emailChangeLinkBaseUrl: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ResolveAuthLinkBaseUrlsInput {
|
|
8
|
+
/** The raw environment (`NODE_ENV`, `RESET_LINK_BASE_URL`, `EMAIL_CHANGE_LINK_BASE_URL`). */
|
|
9
|
+
readonly env: NodeJS.ProcessEnv;
|
|
10
|
+
/** `ShieldModule.forRoot({ emailChangeLinkBaseUrl })`. */
|
|
11
|
+
readonly emailChangeLinkBaseUrlOption?: string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* `true` when no app-supplied sender exists: `emailSender: 'disabled'` (nothing is delivered) or
|
|
14
|
+
* omitted (outside development/test the email-sender boot rule already fails the boot with its
|
|
15
|
+
* own error; in development/test the link rule is off anyway).
|
|
16
|
+
*/
|
|
17
|
+
readonly mailDisabled: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolve both link bases and apply the boot rule. Throws (boot fails) outside development/test
|
|
21
|
+
* when a base is unset or local and mail is not `'disabled'`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveAuthLinkBaseUrls(input: ResolveAuthLinkBaseUrlsInput): AuthLinkBaseUrls;
|
|
24
|
+
//# sourceMappingURL=link-base-url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-base-url.d.ts","sourceRoot":"","sources":["../../src/auth/link-base-url.ts"],"names":[],"mappings":"AAmBA,uDAAuD;AACvD,eAAO,MAAM,2BAA2B,yCAAyC,CAAC;AAElF,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;CACzC;AAED,MAAM,WAAW,4BAA4B;IAC3C,6FAA6F;IAC7F,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,0DAA0D;IAC1D,QAAQ,CAAC,4BAA4B,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3D;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAChC;AAmCD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,4BAA4B,GAAG,gBAAgB,CAgC7F"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_RESET_LINK_BASE_URL = void 0;
|
|
4
|
+
exports.resolveAuthLinkBaseUrls = resolveAuthLinkBaseUrls;
|
|
5
|
+
/**
|
|
6
|
+
* The two link bases the framework mails (security hardening Phase 13 #1):
|
|
7
|
+
* - the password-reset link `${RESET_LINK_BASE_URL}?token=…` (`POST /auth/forgot-password`);
|
|
8
|
+
* - the email-change confirm link `${emailChangeLinkBaseUrl}#t=…` (`PATCH /account/profile`).
|
|
9
|
+
*
|
|
10
|
+
* Precedence: the `emailChangeLinkBaseUrl` option > `EMAIL_CHANGE_LINK_BASE_URL` > dev default;
|
|
11
|
+
* the reset link: `RESET_LINK_BASE_URL` > dev default. A blank value counts as unset.
|
|
12
|
+
*
|
|
13
|
+
* Boot rule (fail closed): outside `NODE_ENV` development/test (raw value, unset is NOT dev), with
|
|
14
|
+
* a mail sender, each base must be set, parse as an `http:`/`https:` URL and name a host that is
|
|
15
|
+
* not loopback/unspecified (`localhost`, `*.localhost`, `127.0.0.0/8`, `::1`, `0.0.0.0`, `::`).
|
|
16
|
+
* Otherwise auth composition throws, naming the variable(s) — never the configured value (it may
|
|
17
|
+
* carry credentials). `emailSender: 'disabled'` skips the rule: the no-op sender delivers nothing,
|
|
18
|
+
* so no link can reach a user (an omitted sender fails the boot earlier, see `auth.module.ts`).
|
|
19
|
+
* `https:` is not required (the reset path never required it).
|
|
20
|
+
*/
|
|
21
|
+
const app_config_js_1 = require("../config/app-config.js");
|
|
22
|
+
const email_change_messages_js_1 = require("./email-change-messages.js");
|
|
23
|
+
/** The default reset-page base (a dev Vite origin). */
|
|
24
|
+
exports.DEFAULT_RESET_LINK_BASE_URL = 'http://localhost:5173/reset-password';
|
|
25
|
+
function present(value) {
|
|
26
|
+
return value !== undefined && value.trim() !== '' ? value.trim() : undefined;
|
|
27
|
+
}
|
|
28
|
+
function isLoopbackOrUnspecifiedHost(hostname) {
|
|
29
|
+
const host = hostname
|
|
30
|
+
.toLowerCase()
|
|
31
|
+
.replace(/^\[|\]$/gu, '')
|
|
32
|
+
.replace(/\.$/u, '');
|
|
33
|
+
if (host === 'localhost' || host.endsWith('.localhost'))
|
|
34
|
+
return true;
|
|
35
|
+
if (host === '0.0.0.0' || host === '::' || host === '::1')
|
|
36
|
+
return true;
|
|
37
|
+
if (/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/u.test(host))
|
|
38
|
+
return true;
|
|
39
|
+
// IPv4-mapped loopback/unspecified (`::ffff:127.0.0.1` normalises to `::ffff:7f00:1`).
|
|
40
|
+
if (/^::ffff:(7f[0-9a-f]{2}:[0-9a-f]{1,4}|0:0)$/u.test(host))
|
|
41
|
+
return true;
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
/** Why a production link base is unusable, or `null` when it is fine. */
|
|
45
|
+
function problemWith(value) {
|
|
46
|
+
if (value === undefined)
|
|
47
|
+
return 'is not set';
|
|
48
|
+
let url;
|
|
49
|
+
try {
|
|
50
|
+
url = new URL(value);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return 'is not a valid URL';
|
|
54
|
+
}
|
|
55
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:')
|
|
56
|
+
return 'is not an http(s) URL';
|
|
57
|
+
if (isLoopbackOrUnspecifiedHost(url.hostname)) {
|
|
58
|
+
return 'points at a loopback/unspecified host (localhost, 127.0.0.1, ::1, 0.0.0.0)';
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Resolve both link bases and apply the boot rule. Throws (boot fails) outside development/test
|
|
64
|
+
* when a base is unset or local and mail is not `'disabled'`.
|
|
65
|
+
*/
|
|
66
|
+
function resolveAuthLinkBaseUrls(input) {
|
|
67
|
+
const resetConfigured = present(input.env.RESET_LINK_BASE_URL);
|
|
68
|
+
const optionConfigured = present(input.emailChangeLinkBaseUrlOption);
|
|
69
|
+
const emailChangeConfigured = optionConfigured ?? present(input.env.EMAIL_CHANGE_LINK_BASE_URL);
|
|
70
|
+
if (!input.mailDisabled && !(0, app_config_js_1.isDevOrTestNodeEnv)(input.env.NODE_ENV)) {
|
|
71
|
+
const problems = [];
|
|
72
|
+
const resetProblem = problemWith(resetConfigured);
|
|
73
|
+
if (resetProblem)
|
|
74
|
+
problems.push(`RESET_LINK_BASE_URL ${resetProblem}`);
|
|
75
|
+
const changeProblem = problemWith(emailChangeConfigured);
|
|
76
|
+
if (changeProblem) {
|
|
77
|
+
const name = optionConfigured
|
|
78
|
+
? 'the emailChangeLinkBaseUrl option'
|
|
79
|
+
: 'EMAIL_CHANGE_LINK_BASE_URL (or the emailChangeLinkBaseUrl option)';
|
|
80
|
+
problems.push(`${name} ${changeProblem}`);
|
|
81
|
+
}
|
|
82
|
+
if (problems.length > 0) {
|
|
83
|
+
const nodeEnv = input.env.NODE_ENV;
|
|
84
|
+
throw new Error('Astram auth composition: the mailed password-reset and email-change links need a ' +
|
|
85
|
+
`public base URL when NODE_ENV is not 'development' or 'test' (got: ${nodeEnv === undefined ? 'unset' : `'${nodeEnv}'`}): ${problems.join('; ')}. Set them to your app's /reset-password and /confirm-email ` +
|
|
86
|
+
"pages, or pass emailSender: 'disabled' if this deployment sends no email.");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
resetLinkBaseUrl: resetConfigured ?? exports.DEFAULT_RESET_LINK_BASE_URL,
|
|
91
|
+
emailChangeLinkBaseUrl: emailChangeConfigured ?? email_change_messages_js_1.DEFAULT_EMAIL_CHANGE_LINK_BASE_URL,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -58,7 +58,9 @@ export interface EmailAddressChanger {
|
|
|
58
58
|
* refuse a collision (another user's `u_email`, another principal's `ic_identity_hint`;
|
|
59
59
|
* collation-equal, locked reads, self excluded), write `u_email`, move `ic_identity_hint` when it
|
|
60
60
|
* equalled the OLD email (a username hint is untouched), and burn the user's pending
|
|
61
|
-
* email-change tokens.
|
|
61
|
+
* email-change tokens. A unique-index refusal of the write (`uq_users_email` or the hint PK:
|
|
62
|
+
* a row written meanwhile by a path that skipped the collision read) is `unavailable` too.
|
|
63
|
+
* Throws on any other store error (caller → 503).
|
|
62
64
|
*/
|
|
63
65
|
change(request: EmailChangeRequest): Promise<EmailChangeOutcome>;
|
|
64
66
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email-address-changer.d.ts","sourceRoot":"","sources":["../../../src/auth/ports/email-address-changer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,+DAA+D;AAC/D,MAAM,WAAW,YAAY;IAC3B,uDAAuD;IACvD,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,qFAAqF;IACrF,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC;AAED,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAErF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,mFAAmF;IACnF,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAC3B,KAAK,EAAE,gBAAgB,EACvB,MAAM,EAAE;QAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;KAAE,KACtE,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAAE;AAC/F,2FAA2F;GACzF;IAAE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;CAAE,GAClC;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAChC;IAAE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GAC/B;IAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAEjC,MAAM,WAAW,mBAAmB;IAClC,kGAAkG;IAClG,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAClF
|
|
1
|
+
{"version":3,"file":"email-address-changer.d.ts","sourceRoot":"","sources":["../../../src/auth/ports/email-address-changer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,+DAA+D;AAC/D,MAAM,WAAW,YAAY;IAC3B,uDAAuD;IACvD,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,qFAAqF;IACrF,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC;AAED,4EAA4E;AAC5E,MAAM,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAErF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,mFAAmF;IACnF,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAC3B,KAAK,EAAE,gBAAgB,EACvB,MAAM,EAAE;QAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;KAAE,KACtE,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAAE;AAC/F,2FAA2F;GACzF;IAAE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;CAAE,GAClC;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAChC;IAAE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GAC/B;IAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAEjC,MAAM,WAAW,mBAAmB;IAClC,kGAAkG;IAClG,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAClF;;;;;;;;OAQG;IACH,MAAM,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CAClE"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { CurrentUserProfile } from '../profile-resolver.js';
|
|
2
2
|
/**
|
|
3
|
-
* ProfileWriter — self-service profile edit
|
|
3
|
+
* ProfileWriter — self-service profile edit. Returns the refreshed
|
|
4
4
|
* display profile (names, never UUIDs). Consumer-plugged.
|
|
5
|
+
*
|
|
6
|
+
* Since Phase 12 (KI #14) `PATCH /account/profile` passes only `name`: an email change is
|
|
7
|
+
* requested, confirmed and written through the verified `EmailAddressChanger` flow. `email`
|
|
8
|
+
* stays in the type for compatibility; the framework default refuses it.
|
|
5
9
|
*/
|
|
6
10
|
export interface ProfileWriter {
|
|
7
11
|
update(principalId: string, changes: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profile-writer.d.ts","sourceRoot":"","sources":["../../../src/auth/ports/profile-writer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE
|
|
1
|
+
{"version":3,"file":"profile-writer.d.ts","sourceRoot":"","sources":["../../../src/auth/ports/profile-writer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,CACJ,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GACzC,OAAO,CAAC,kBAAkB,CAAC,CAAC;CAChC"}
|
package/dist/auth/tokens.d.ts
CHANGED
|
@@ -99,7 +99,8 @@ export declare const SHIELD_EMAIL_CHANGE_TOKEN_STORE: unique symbol;
|
|
|
99
99
|
export declare const SHIELD_EMAIL_ADDRESS_CHANGER: unique symbol;
|
|
100
100
|
/**
|
|
101
101
|
* The email-change confirm-link base URL (a plain `string`, `ShieldModule.forRoot({
|
|
102
|
-
* emailChangeLinkBaseUrl })
|
|
102
|
+
* emailChangeLinkBaseUrl })` > env `EMAIL_CHANGE_LINK_BASE_URL` > dev default
|
|
103
|
+
* `http://localhost:5173/confirm-email`; production boot rule in `link-base-url.ts`). The link is
|
|
103
104
|
* `${base}#t=<rawToken>` — the token rides in the FRAGMENT, never the query (A4), so it is not
|
|
104
105
|
* sent to the server that serves the page nor leaked through a Referer.
|
|
105
106
|
*/
|
|
@@ -131,7 +132,8 @@ export declare const SHIELD_EMAIL_SENDER: unique symbol;
|
|
|
131
132
|
export declare const SHIELD_RECOVERY_CODE_STORE: unique symbol;
|
|
132
133
|
/**
|
|
133
134
|
* The `ProfileWriter` (guard port, `../auth/ports/profile-writer.ts`) — the
|
|
134
|
-
* self-service profile-edit seam (name
|
|
135
|
+
* self-service profile-edit seam (name; an email goes through the verified email-change flow).
|
|
136
|
+
* Consumed by `PATCH /account/profile`.
|
|
135
137
|
*/
|
|
136
138
|
export declare const SHIELD_PROFILE_WRITER: unique symbol;
|
|
137
139
|
/**
|
|
@@ -164,7 +166,8 @@ export declare const SHIELD_SESSION_SWEEPER: unique symbol;
|
|
|
164
166
|
* composes the emailed reset link as `${base}?token=<rawToken>` so the
|
|
165
167
|
* {@link SHIELD_EMAIL_SENDER} seam stays a dumb delivery channel (no URL/token
|
|
166
168
|
* logic). Bound app-wide by `AuthModule` from the environment
|
|
167
|
-
* (`RESET_LINK_BASE_URL`, default `http://localhost:5173/reset-password
|
|
169
|
+
* (`RESET_LINK_BASE_URL`, default `http://localhost:5173/reset-password`; outside
|
|
170
|
+
* development/test an unset or loopback value fails the boot, see `link-base-url.ts`). Consumed
|
|
168
171
|
* by `POST /auth/forgot-password`.
|
|
169
172
|
*/
|
|
170
173
|
export declare const SHIELD_RESET_LINK_BASE_URL: unique symbol;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/auth/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,+CAA+C,CAAC;AAEvF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,eAA2B,CAAC;AAEvD;;;;GAIG;AACH,eAAO,MAAM,YAAY,eAAyB,CAAC;AAEnD;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC;AAE/E;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,eAAiC,CAAC;AAEnE;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,eAAqC,CAAC;AAW3E;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,eAAqC,CAAC;AAE3E;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,eAAqC,CAAC;AAE3E;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,eAA4C,CAAC;AAEzF;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,eAAyC,CAAC;AAEnF
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/auth/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,+CAA+C,CAAC;AAEvF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,eAA2B,CAAC;AAEvD;;;;GAIG;AACH,eAAO,MAAM,YAAY,eAAyB,CAAC;AAEnD;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC;AAE/E;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,eAAiC,CAAC;AAEnE;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,eAAqC,CAAC;AAW3E;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,eAAqC,CAAC;AAE3E;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,eAAqC,CAAC;AAE3E;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,eAA4C,CAAC;AAEzF;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,eAAyC,CAAC;AAEnF;;;;;;GAMG;AACH,eAAO,MAAM,iCAAiC,eAA8C,CAAC;AAE7F;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,eAAwC,CAAC;AAEjF;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,eAAyC,CAAC;AAEnF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,eAAgC,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC;AAE/E;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,eAAkC,CAAC;AAErE;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,eAA8B,CAAC;AAE7D;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,eAAmC,CAAC;AAEvE;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC;AAE/E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,eAAiC,CAAC;AAEnE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,6BAA6B,eAA0C,CAAC;AAErF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,eAA4B,CAAC;AAEzD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,eAA2C,CAAC;AAEvF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,6BAA6B,eAA0C,CAAC;AAErF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,eAAsC,CAAC;AAU7E;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,eAAiC,CAAC;AAEnE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAEzE;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAEzE;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,eAA+B,CAAC;AAE/D;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,eAA+B,CAAC;AAE/D;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC;AAE/E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,eAAkC,CAAC;AAErE;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,eAAuC,CAAC;AAE/E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,eAAiC,CAAC;AAEnE;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAEzE;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,eAA4C,CAAC;AAEzF;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,eAAwC,CAAC"}
|
package/dist/auth/tokens.js
CHANGED
|
@@ -111,7 +111,8 @@ exports.SHIELD_EMAIL_CHANGE_TOKEN_STORE = Symbol('SHIELD_EMAIL_CHANGE_TOKEN_STOR
|
|
|
111
111
|
exports.SHIELD_EMAIL_ADDRESS_CHANGER = Symbol('SHIELD_EMAIL_ADDRESS_CHANGER');
|
|
112
112
|
/**
|
|
113
113
|
* The email-change confirm-link base URL (a plain `string`, `ShieldModule.forRoot({
|
|
114
|
-
* emailChangeLinkBaseUrl })
|
|
114
|
+
* emailChangeLinkBaseUrl })` > env `EMAIL_CHANGE_LINK_BASE_URL` > dev default
|
|
115
|
+
* `http://localhost:5173/confirm-email`; production boot rule in `link-base-url.ts`). The link is
|
|
115
116
|
* `${base}#t=<rawToken>` — the token rides in the FRAGMENT, never the query (A4), so it is not
|
|
116
117
|
* sent to the server that serves the page nor leaked through a Referer.
|
|
117
118
|
*/
|
|
@@ -143,7 +144,8 @@ exports.SHIELD_EMAIL_SENDER = Symbol('SHIELD_EMAIL_SENDER');
|
|
|
143
144
|
exports.SHIELD_RECOVERY_CODE_STORE = Symbol('SHIELD_RECOVERY_CODE_STORE');
|
|
144
145
|
/**
|
|
145
146
|
* The `ProfileWriter` (guard port, `../auth/ports/profile-writer.ts`) — the
|
|
146
|
-
* self-service profile-edit seam (name
|
|
147
|
+
* self-service profile-edit seam (name; an email goes through the verified email-change flow).
|
|
148
|
+
* Consumed by `PATCH /account/profile`.
|
|
147
149
|
*/
|
|
148
150
|
exports.SHIELD_PROFILE_WRITER = Symbol('SHIELD_PROFILE_WRITER');
|
|
149
151
|
/**
|
|
@@ -176,7 +178,8 @@ exports.SHIELD_SESSION_SWEEPER = Symbol('SHIELD_SESSION_SWEEPER');
|
|
|
176
178
|
* composes the emailed reset link as `${base}?token=<rawToken>` so the
|
|
177
179
|
* {@link SHIELD_EMAIL_SENDER} seam stays a dumb delivery channel (no URL/token
|
|
178
180
|
* logic). Bound app-wide by `AuthModule` from the environment
|
|
179
|
-
* (`RESET_LINK_BASE_URL`, default `http://localhost:5173/reset-password
|
|
181
|
+
* (`RESET_LINK_BASE_URL`, default `http://localhost:5173/reset-password`; outside
|
|
182
|
+
* development/test an unset or loopback value fails the boot, see `link-base-url.ts`). Consumed
|
|
180
183
|
* by `POST /auth/forgot-password`.
|
|
181
184
|
*/
|
|
182
185
|
exports.SHIELD_RESET_LINK_BASE_URL = Symbol('SHIELD_RESET_LINK_BASE_URL');
|
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
* paths share one collision rule and one login-hint rule.
|
|
7
7
|
*
|
|
8
8
|
* `change` (one MariaDB transaction, lock-then-recheck):
|
|
9
|
-
* 0. `GET_LOCK('shield_email_change')` — serialises email changes.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* 0. `GET_LOCK('shield_email_change')` — serialises email changes. The locked collision reads
|
|
10
|
+
* below look up a value that is normally ABSENT; under REPEATABLE READ that takes a gap lock
|
|
11
|
+
* on `uq_users_email` (migration 021, KI #87) and on the `ic_identity_hint` PK. Gap locks do
|
|
12
|
+
* not conflict with each other but block the other side's insert into the gap, so two
|
|
13
|
+
* concurrent changes into the same gap would deadlock (1213); the named lock prevents that.
|
|
12
14
|
* 1. lock the user row (`FOR UPDATE`); refuse `not_found`, `inactive` (when required), `stale`
|
|
13
15
|
* (stored email ≠ the expected old one, exact compare — A3);
|
|
14
16
|
* 2. refuse `unavailable` when another user's `u_email` or another principal's login hint is
|
|
@@ -18,6 +20,11 @@
|
|
|
18
20
|
* 4. login-hint follow: `ic_identity_hint` (the PK) is updated IN PLACE to the new address when it
|
|
19
21
|
* equalled the OLD email (collation-equal); a username hint matches nothing and stays;
|
|
20
22
|
* 5. burn the user's pending email-change tokens; run `withinTransaction`; commit.
|
|
23
|
+
* The unique index is the last word for writers that do not take the named lock (a direct insert,
|
|
24
|
+
* a user create): a duplicate key (1062) on step 3 or 4 is the same answer as step 2,
|
|
25
|
+
* `unavailable`, rolled back. The driver's message names the full address, so it is dropped here
|
|
26
|
+
* and never logged. Any other store error (a 1213 deadlock, a lock-wait timeout) is thrown
|
|
27
|
+
* (caller → 503).
|
|
21
28
|
* Every query is parameterized; addresses are compared by the column collation in SQL.
|
|
22
29
|
*/
|
|
23
30
|
import type { Pool } from 'mariadb';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mariadb-email-address-changer.d.ts","sourceRoot":"","sources":["../../src/authentication/mariadb-email-address-changer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"mariadb-email-address-changer.d.ts","sourceRoot":"","sources":["../../src/authentication/mariadb-email-address-changer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,KAAK,EAAE,IAAI,EAAkB,MAAM,SAAS,CAAC;AACpD,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,wCAAwC,CAAC;AAqBhD,qBAAa,0BAA2B,YAAW,mBAAmB;IAElE,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,GAAG;gBADH,IAAI,EAAE,IAAI,EACV,GAAG,GAAE,MAAM,MAAiB;IAGzC,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAejF,MAAM,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC;YA6BxD,YAAY;CAqE3B"}
|
|
@@ -3,6 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.MariaDbEmailAddressChanger = void 0;
|
|
4
4
|
/** Seconds to wait for the email-change named lock before giving up (caller → 503). */
|
|
5
5
|
const LOCK_TIMEOUT_S = 10;
|
|
6
|
+
/** MariaDB `ER_DUP_ENTRY`: a unique index (here `uq_users_email` or the hint PK) refused a write. */
|
|
7
|
+
const ER_DUP_ENTRY = 1062;
|
|
8
|
+
/** A refusal by a unique index, recognised by errno/code only (the message holds the value). */
|
|
9
|
+
function isDuplicateKey(err) {
|
|
10
|
+
if (typeof err !== 'object' || err === null)
|
|
11
|
+
return false;
|
|
12
|
+
const e = err;
|
|
13
|
+
return e.errno === ER_DUP_ENTRY || e.code === 'ER_DUP_ENTRY';
|
|
14
|
+
}
|
|
6
15
|
class MariaDbEmailAddressChanger {
|
|
7
16
|
pool;
|
|
8
17
|
now;
|
|
@@ -75,16 +84,25 @@ class MariaDbEmailAddressChanger {
|
|
|
75
84
|
return { status: 'unavailable' };
|
|
76
85
|
const selfAlreadyHasNewHint = hintOwners.length > 0;
|
|
77
86
|
await conn.query('SELECT ic_identity_hint FROM `shield_identity`.`identity_credential` WHERE ic_principal_id = ? FOR UPDATE', [principalId]);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
principalId,
|
|
81
|
-
]);
|
|
87
|
+
// Steps 3–4: a duplicate key here is a row the locked reads could not see (written by a path
|
|
88
|
+
// without the named lock); the index refuses it → `unavailable`, and `change` rolls back.
|
|
82
89
|
let hintMoved = false;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
try {
|
|
91
|
+
await conn.query('UPDATE `shield_app`.`users` SET u_email = ? WHERE u_uuid = ?', [
|
|
92
|
+
newEmail,
|
|
93
|
+
principalId,
|
|
94
|
+
]);
|
|
95
|
+
if (oldEmail !== null && oldEmail !== '' && !selfAlreadyHasNewHint) {
|
|
96
|
+
const moved = (await conn.query(`UPDATE \`shield_identity\`.\`identity_credential\`
|
|
97
|
+
SET ic_identity_hint = ?, ic_updated_at_ms = ?
|
|
98
|
+
WHERE ic_principal_id = ? AND ic_identity_hint = ?`, [newEmail, this.now(), principalId, oldEmail]));
|
|
99
|
+
hintMoved = moved.affectedRows > 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
if (isDuplicateKey(err))
|
|
104
|
+
return { status: 'unavailable' }; // the driver message is dropped
|
|
105
|
+
throw err;
|
|
88
106
|
}
|
|
89
107
|
await conn.query(`UPDATE \`shield_identity\`.\`email_change_token\` SET ect_consumed_at_ms = ?
|
|
90
108
|
WHERE ect_principal_id = ? AND ect_consumed_at_ms IS NULL`, [this.now(), principalId]);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MariaDbProfileWriter — the framework-DEFAULT ProfileWriter (backs PATCH /account/profile). Updates
|
|
3
|
-
* ONLY the supplied
|
|
3
|
+
* ONLY the supplied name (`u_username`) on shield_app.users — an `email` is refused (it goes through
|
|
4
|
+
* the verified email-change flow, Phase 12 / KI #87) — then re-reads via MariaDbProfileResolver so the returned
|
|
4
5
|
* display profile has ONE source of truth (names, never UUIDs). Fail-closed: throws if the re-read is null.
|
|
5
6
|
*/
|
|
6
7
|
import type { Pool } from 'mariadb';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mariadb-profile-writer.d.ts","sourceRoot":"","sources":["../../src/authentication/mariadb-profile-writer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"mariadb-profile-writer.d.ts","sourceRoot":"","sources":["../../src/authentication/mariadb-profile-writer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAOtE,qBAAa,oBAAqB,YAAW,aAAa;IAE5C,OAAO,CAAC,QAAQ,CAAC,IAAI;IADjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyB;gBACrB,IAAI,EAAE,IAAI;IAIjC,MAAM,CACV,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GACzC,OAAO,CAAC,kBAAkB,CAAC;CA0C/B"}
|
|
@@ -10,16 +10,18 @@ class MariaDbProfileWriter {
|
|
|
10
10
|
this.resolver = new mariadb_profile_resolver_js_1.MariaDbProfileResolver(pool);
|
|
11
11
|
}
|
|
12
12
|
async update(principalId, changes) {
|
|
13
|
+
// Phase 12 / KI #87: the email is written ONLY by the verified email-change flow
|
|
14
|
+
// (`MariaDbEmailAddressChanger`: collision refusal, login-hint follow, `uq_users_email`
|
|
15
|
+
// mapping). `PATCH /account/profile` never passes it here; refuse rather than write unchecked.
|
|
16
|
+
if (changes.email !== undefined) {
|
|
17
|
+
throw new Error('MariaDbProfileWriter: email changes go through the verified email-change flow only');
|
|
18
|
+
}
|
|
13
19
|
const sets = [];
|
|
14
20
|
const params = [];
|
|
15
21
|
if (changes.name !== undefined) {
|
|
16
22
|
sets.push('u_username = ?');
|
|
17
23
|
params.push(changes.name);
|
|
18
24
|
}
|
|
19
|
-
if (changes.email !== undefined) {
|
|
20
|
-
sets.push('u_email = ?');
|
|
21
|
-
params.push(changes.email);
|
|
22
|
-
}
|
|
23
25
|
if (sets.length > 0) {
|
|
24
26
|
params.push(principalId);
|
|
25
27
|
await this.pool.query(`UPDATE \`shield_app\`.\`users\` SET ${sets.join(', ')} WHERE u_uuid = ?`, params);
|
|
@@ -190,8 +190,10 @@ export interface ShieldModuleOptions {
|
|
|
190
190
|
/**
|
|
191
191
|
* The base URL of the app's confirm-email page (security hardening Phase 12, KI #14). A
|
|
192
192
|
* self-service email change mails `${emailChangeLinkBaseUrl}#t=<token>` to the NEW address — the
|
|
193
|
-
* token in the fragment, never the query.
|
|
194
|
-
* consulted when `auth: true`.
|
|
193
|
+
* token in the fragment, never the query. Option > env `EMAIL_CHANGE_LINK_BASE_URL` > dev default
|
|
194
|
+
* `http://localhost:5173/confirm-email`. Only consulted when `auth: true`. Outside `NODE_ENV`
|
|
195
|
+
* development/test the boot FAILS when this base (or `RESET_LINK_BASE_URL`) is unset, invalid or
|
|
196
|
+
* loopback (`localhost`, `127.0.0.1`, `::1`, `0.0.0.0`), unless `emailSender: 'disabled'`.
|
|
195
197
|
*/
|
|
196
198
|
readonly emailChangeLinkBaseUrl?: string;
|
|
197
199
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shield.module.d.ts","sourceRoot":"","sources":["../../src/module/shield.module.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"shield.module.d.ts","sourceRoot":"","sources":["../../src/module/shield.module.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astram/guard",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Astram framework — the enforcement guard + real-world adapters (the 'hands'). One global deny-by-default gate every request passes through (ADR-0002), classification decorators, boot-pool assertion (ADR-0018), the forRoot() registration seam, and the DB/Redis/crypto adapters that connect the sealed core to a real backend. Depends on @astram/core (ADR-0003, ADR-0023).",
|
|
6
6
|
"type": "commonjs",
|