@aooth/auth-moost 0.1.23 → 0.1.25
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/index.d.mts +108 -6
- package/dist/index.mjs +186 -14
- package/package.json +13 -13
package/dist/index.d.mts
CHANGED
|
@@ -1785,6 +1785,14 @@ interface AuthWfCtx {
|
|
|
1785
1785
|
aborted?: boolean;
|
|
1786
1786
|
isFirstLogin?: boolean;
|
|
1787
1787
|
newPasswordRequired?: boolean;
|
|
1788
|
+
/**
|
|
1789
|
+
* Idempotency latch for the single `record-login` funnel step. Set true once
|
|
1790
|
+
* a login has been stamped this run — by the `credentials` step (the password
|
|
1791
|
+
* path stamps eagerly via `users.login()`) or by `record-login` itself — so
|
|
1792
|
+
* the funnel never double-writes `account.lastLogin`. Server-only flow
|
|
1793
|
+
* control; never `@wf.context.pass`-ed to the client.
|
|
1794
|
+
*/
|
|
1795
|
+
loginRecorded?: boolean;
|
|
1788
1796
|
autoLogin?: boolean;
|
|
1789
1797
|
consents?: AuthWfConsentsState;
|
|
1790
1798
|
pincode?: AuthWfPincodeUiState;
|
|
@@ -2124,6 +2132,47 @@ declare class AuthWorkflow {
|
|
|
2124
2132
|
* mirrors `notifyNewDevice`'s posture). Never called by the base class.
|
|
2125
2133
|
*/
|
|
2126
2134
|
protected sendSecurityAlert(ctx: AuthWfCtx, reason: string, context?: Record<string, unknown>): Promise<void>;
|
|
2135
|
+
/**
|
|
2136
|
+
* A user just authenticated and a session is being established — interactive
|
|
2137
|
+
* login, federated (SSO) login, OR invite/signup/recovery auto-login. Fired
|
|
2138
|
+
* from the single `record-login` funnel, AFTER `account.lastLogin` is stamped
|
|
2139
|
+
* and BEFORE the session/code is delivered, so a throw aborts the login
|
|
2140
|
+
* atomically (no half-issued session). `ctx.subject` is set; read
|
|
2141
|
+
* `ctx.isFirstLogin` to distinguish the very first sign-in, `ctx.oauth` for
|
|
2142
|
+
* federated context. Does NOT fire for the no-session "fresh-login" finalize
|
|
2143
|
+
* (where the user is redirected to sign in separately).
|
|
2144
|
+
*/
|
|
2145
|
+
protected afterLogin(_ctx: AuthWfCtx): void | Promise<void>;
|
|
2146
|
+
/**
|
|
2147
|
+
* An invitee finished accepting their invite — account activated — whether or
|
|
2148
|
+
* not the flow auto-logged-them-in. Fires once, before the finalize terminal.
|
|
2149
|
+
* (An auto-login invite ALSO fires {@link afterLogin}.)
|
|
2150
|
+
*/
|
|
2151
|
+
protected afterInvitationAccepted(_ctx: AuthWfCtx): void | Promise<void>;
|
|
2152
|
+
/**
|
|
2153
|
+
* A self-signup account was created + activated (post password-set, post
|
|
2154
|
+
* activate). Fires once, before the auto-login finalize. (Signup always
|
|
2155
|
+
* auto-logins, so {@link afterLogin} fires too.)
|
|
2156
|
+
*/
|
|
2157
|
+
protected afterSignup(_ctx: AuthWfCtx): void | Promise<void>;
|
|
2158
|
+
/**
|
|
2159
|
+
* A recovery password reset completed. Fires once even when an `admin-only`
|
|
2160
|
+
* lock survived the reset (the password DID change) — so it runs ahead of the
|
|
2161
|
+
* still-locked guard. An auto-login recovery ALSO fires {@link afterLogin}.
|
|
2162
|
+
*/
|
|
2163
|
+
protected afterPasswordReset(_ctx: AuthWfCtx): void | Promise<void>;
|
|
2164
|
+
/**
|
|
2165
|
+
* An already-authenticated user changed their own password (change-password
|
|
2166
|
+
* flow). NOT a login — the session is rotated, not established — so
|
|
2167
|
+
* {@link afterLogin} does NOT fire.
|
|
2168
|
+
*/
|
|
2169
|
+
protected afterPasswordChanged(_ctx: AuthWfCtx): void | Promise<void>;
|
|
2170
|
+
/**
|
|
2171
|
+
* A user added, changed, or removed an MFA factor (add-mfa flow). NOT fired
|
|
2172
|
+
* on a cancel / nothing-to-do finish. The user KEEPS their session (no
|
|
2173
|
+
* re-issue), so {@link afterLogin} does NOT fire.
|
|
2174
|
+
*/
|
|
2175
|
+
protected afterMfaChanged(_ctx: AuthWfCtx): void | Promise<void>;
|
|
2127
2176
|
/**
|
|
2128
2177
|
* Return the list of selectable role identifiers for the admin invite form.
|
|
2129
2178
|
* Mirrors the prior `InviteWorkflow.getAvailableRoles()` consumer hook —
|
|
@@ -2994,7 +3043,7 @@ declare class AuthWorkflow {
|
|
|
2994
3043
|
* (un-removable operation aborted by `confirm-remove-mfa`) →
|
|
2995
3044
|
* nothing-available (zero candidates, never had to step-up) → cancelled.
|
|
2996
3045
|
*/
|
|
2997
|
-
finishAddMfa(ctx: AuthWfCtx): undefined
|
|
3046
|
+
finishAddMfa(ctx: AuthWfCtx): undefined | Promise<undefined>;
|
|
2998
3047
|
/**
|
|
2999
3048
|
* `finish-add-mfa`'s envelope construction, extracted pure so the outcome
|
|
3000
3049
|
* priority (removed → changed → added → blocked → nothing-available →
|
|
@@ -3349,6 +3398,52 @@ declare class AuthWorkflow {
|
|
|
3349
3398
|
* also zeroes `failedLoginAttempts`, so the next login starts clean.
|
|
3350
3399
|
*/
|
|
3351
3400
|
unlockAccount(ctx: AuthWfCtx): Promise<undefined>;
|
|
3401
|
+
/**
|
|
3402
|
+
* The SINGLE login-completion funnel. Every flow that establishes an
|
|
3403
|
+
* authenticated session routes through here — placed in each login schema
|
|
3404
|
+
* right after the guards and BEFORE the delivery terminal (`issue` /
|
|
3405
|
+
* `mint-authz-code` / `finalize-auto-login`) — so a login can never be
|
|
3406
|
+
* delivered without passing this point. Two uniform jobs:
|
|
3407
|
+
* 1. Stamp `account.lastLogin` exactly once (the sole workflow writer of it).
|
|
3408
|
+
* 2. Fire the `afterLogin` customer hook.
|
|
3409
|
+
*
|
|
3410
|
+
* Idempotent per run via `ctx.loginRecorded`: the password `credentials` path
|
|
3411
|
+
* already stamped eagerly through `users.login()` and latched the flag, so the
|
|
3412
|
+
* stamp NO-OPS there (no double write) — but `afterLogin` still fires, so the
|
|
3413
|
+
* hook runs exactly once for password logins too. Federated (`sso-callback`)
|
|
3414
|
+
* and auto-login (invite/signup/recovery) paths never call `login()`, so this
|
|
3415
|
+
* is their sole stamp. Self-gates on `ctx.subject`. Runs AFTER
|
|
3416
|
+
* `prepare-semantic-flags` derived `isFirstLogin`, so a genuine first
|
|
3417
|
+
* federated login still observes `isFirstLogin === true`.
|
|
3418
|
+
*
|
|
3419
|
+
* A throw (from the stamp or the hook) aborts the flow BEFORE delivery, so the
|
|
3420
|
+
* login fails atomically — no half-issued session.
|
|
3421
|
+
*/
|
|
3422
|
+
recordLogin(ctx: AuthWfCtx): undefined | Promise<undefined>;
|
|
3423
|
+
/**
|
|
3424
|
+
* Recovery-only guard, extracted from the finalize terminals so they stay pure
|
|
3425
|
+
* delivery. An `admin-only` lockout never self-unlocks, so a reset that left
|
|
3426
|
+
* the account frozen must NOT be confirmed-as-success OR auto-logged-in
|
|
3427
|
+
* (minting tokens would defeat the very freeze). When still locked it emits the
|
|
3428
|
+
* warn terminal and sets `ctx.aborted`, so the schema's following `{ break }`
|
|
3429
|
+
* skips BOTH the `record-login` funnel and the finalize terminals — a frozen
|
|
3430
|
+
* account is never stamped or logged in. The schema gates this on
|
|
3431
|
+
* `ctx.lockout?.mode === "admin-only"` (the only mode that can reach finalize
|
|
3432
|
+
* still locked: self-service ran `unlock-account`; temporary auto-expires).
|
|
3433
|
+
*/
|
|
3434
|
+
recoveryLockCheck(ctx: AuthWfCtx): Promise<undefined>;
|
|
3435
|
+
/**
|
|
3436
|
+
* Thin dispatcher steps for the flow-specific lifecycle hooks. Each is its own
|
|
3437
|
+
* schema step (rather than an in-terminal call) because its flow's finalize
|
|
3438
|
+
* terminals are SHARED across flows (`finalize-auto-login` serves invite +
|
|
3439
|
+
* recovery + signup; `finalize-fresh-login` serves invite + recovery), so a
|
|
3440
|
+
* dedicated step in the flow-specific schema fires the right hook without
|
|
3441
|
+
* ctx-discrimination inside a shared terminal. Named `fire*` so the overridable
|
|
3442
|
+
* `after*` hooks keep the clean public name.
|
|
3443
|
+
*/
|
|
3444
|
+
fireInvitationAccepted(ctx: AuthWfCtx): void | Promise<void>;
|
|
3445
|
+
fireSignup(ctx: AuthWfCtx): void | Promise<void>;
|
|
3446
|
+
firePasswordReset(ctx: AuthWfCtx): void | Promise<void>;
|
|
3352
3447
|
/**
|
|
3353
3448
|
* Issue access + refresh tokens via `auth.issue`. Stashes the login
|
|
3354
3449
|
* response envelope on `useWfFinished` so downstream `redirect` can
|
|
@@ -3422,7 +3517,7 @@ declare class AuthWorkflow {
|
|
|
3422
3517
|
* confirmation first. Discriminated by ctx-slot presence
|
|
3423
3518
|
* (`ctx.postReset` → recovery; otherwise invite).
|
|
3424
3519
|
*/
|
|
3425
|
-
finalizeFreshLogin(ctx: AuthWfCtx): undefined
|
|
3520
|
+
finalizeFreshLogin(ctx: AuthWfCtx): undefined;
|
|
3426
3521
|
/**
|
|
3427
3522
|
* Post-reset store read: did an `admin-only` lockout survive the password
|
|
3428
3523
|
* reset (account still frozen)? Callers MUST first confirm
|
|
@@ -3443,10 +3538,17 @@ declare class AuthWorkflow {
|
|
|
3443
3538
|
*/
|
|
3444
3539
|
private finishRecoveryReset;
|
|
3445
3540
|
/**
|
|
3446
|
-
* Auto-login finalize — invite + recovery
|
|
3447
|
-
* and stashes the login response envelope on
|
|
3448
|
-
* preserves any `message` set by an earlier terminal
|
|
3449
|
-
* the SPA paints the confirmation text alongside the
|
|
3541
|
+
* Auto-login finalize — invite + recovery + signup. PURE delivery: issues
|
|
3542
|
+
* access + refresh tokens and stashes the login response envelope on
|
|
3543
|
+
* `useWfFinished`. Invite preserves any `message` set by an earlier terminal
|
|
3544
|
+
* (`confirmation`) so the SPA paints the confirmation text alongside the
|
|
3545
|
+
* tokens (WF-INVITE-020).
|
|
3546
|
+
*
|
|
3547
|
+
* It records NOTHING and guards NOTHING: `account.lastLogin` + the
|
|
3548
|
+
* `afterLogin` hook are owned by the upstream `record-login` funnel step, and
|
|
3549
|
+
* the admin-only-survived-lock guard by the upstream `recovery-lock-check`
|
|
3550
|
+
* step — both of which `{ break }`/gate this step out when they apply, so a
|
|
3551
|
+
* still-frozen account never reaches here.
|
|
3450
3552
|
*/
|
|
3451
3553
|
finalizeAutoLogin(ctx: AuthWfCtx): Promise<undefined>;
|
|
3452
3554
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -1240,6 +1240,47 @@ let AuthWorkflow = class AuthWorkflow {
|
|
|
1240
1240
|
});
|
|
1241
1241
|
}
|
|
1242
1242
|
/**
|
|
1243
|
+
* A user just authenticated and a session is being established — interactive
|
|
1244
|
+
* login, federated (SSO) login, OR invite/signup/recovery auto-login. Fired
|
|
1245
|
+
* from the single `record-login` funnel, AFTER `account.lastLogin` is stamped
|
|
1246
|
+
* and BEFORE the session/code is delivered, so a throw aborts the login
|
|
1247
|
+
* atomically (no half-issued session). `ctx.subject` is set; read
|
|
1248
|
+
* `ctx.isFirstLogin` to distinguish the very first sign-in, `ctx.oauth` for
|
|
1249
|
+
* federated context. Does NOT fire for the no-session "fresh-login" finalize
|
|
1250
|
+
* (where the user is redirected to sign in separately).
|
|
1251
|
+
*/
|
|
1252
|
+
afterLogin(_ctx) {}
|
|
1253
|
+
/**
|
|
1254
|
+
* An invitee finished accepting their invite — account activated — whether or
|
|
1255
|
+
* not the flow auto-logged-them-in. Fires once, before the finalize terminal.
|
|
1256
|
+
* (An auto-login invite ALSO fires {@link afterLogin}.)
|
|
1257
|
+
*/
|
|
1258
|
+
afterInvitationAccepted(_ctx) {}
|
|
1259
|
+
/**
|
|
1260
|
+
* A self-signup account was created + activated (post password-set, post
|
|
1261
|
+
* activate). Fires once, before the auto-login finalize. (Signup always
|
|
1262
|
+
* auto-logins, so {@link afterLogin} fires too.)
|
|
1263
|
+
*/
|
|
1264
|
+
afterSignup(_ctx) {}
|
|
1265
|
+
/**
|
|
1266
|
+
* A recovery password reset completed. Fires once even when an `admin-only`
|
|
1267
|
+
* lock survived the reset (the password DID change) — so it runs ahead of the
|
|
1268
|
+
* still-locked guard. An auto-login recovery ALSO fires {@link afterLogin}.
|
|
1269
|
+
*/
|
|
1270
|
+
afterPasswordReset(_ctx) {}
|
|
1271
|
+
/**
|
|
1272
|
+
* An already-authenticated user changed their own password (change-password
|
|
1273
|
+
* flow). NOT a login — the session is rotated, not established — so
|
|
1274
|
+
* {@link afterLogin} does NOT fire.
|
|
1275
|
+
*/
|
|
1276
|
+
afterPasswordChanged(_ctx) {}
|
|
1277
|
+
/**
|
|
1278
|
+
* A user added, changed, or removed an MFA factor (add-mfa flow). NOT fired
|
|
1279
|
+
* on a cancel / nothing-to-do finish. The user KEEPS their session (no
|
|
1280
|
+
* re-issue), so {@link afterLogin} does NOT fire.
|
|
1281
|
+
*/
|
|
1282
|
+
afterMfaChanged(_ctx) {}
|
|
1283
|
+
/**
|
|
1243
1284
|
* Return the list of selectable role identifiers for the admin invite form.
|
|
1244
1285
|
* Mirrors the prior `InviteWorkflow.getAvailableRoles()` consumer hook —
|
|
1245
1286
|
* `undefined` (default) means no whitelist is enforced. Read by
|
|
@@ -2364,6 +2405,7 @@ let AuthWorkflow = class AuthWorkflow {
|
|
|
2364
2405
|
try {
|
|
2365
2406
|
const result = await this.users.login(input.username, input.password, this.lockoutOverride(ctx));
|
|
2366
2407
|
ctx.subject = result.user.id;
|
|
2408
|
+
ctx.loginRecorded = true;
|
|
2367
2409
|
swapStrategy("store");
|
|
2368
2410
|
if (ctx.guards?.passwordInitial && result.user.password.isInitial) ctx.isPasswordInitial = true;
|
|
2369
2411
|
if (ctx.guards?.passwordExpiry && this.users.isPasswordExpired(result.user)) ctx.isPasswordExpired = true;
|
|
@@ -3003,6 +3045,7 @@ let AuthWorkflow = class AuthWorkflow {
|
|
|
3003
3045
|
value: envelope,
|
|
3004
3046
|
cookies: auth.buildFinishedCookies(issue)
|
|
3005
3047
|
});
|
|
3048
|
+
await this.afterPasswordChanged(ctx);
|
|
3006
3049
|
}
|
|
3007
3050
|
/**
|
|
3008
3051
|
* Terminal for the manage-MFA flow. The user KEEPS their current session (no
|
|
@@ -3016,6 +3059,9 @@ let AuthWorkflow = class AuthWorkflow {
|
|
|
3016
3059
|
type: "data",
|
|
3017
3060
|
value: this.buildAddMfaFinishEnvelope(ctx)
|
|
3018
3061
|
});
|
|
3062
|
+
if (!ctx.addMfa?.removed && !(ctx.mfaEnroll?.done && ctx.mfaEnroll?.method)) return void 0;
|
|
3063
|
+
const hook = this.afterMfaChanged(ctx);
|
|
3064
|
+
return hook instanceof Promise ? hook.then(() => void 0) : void 0;
|
|
3019
3065
|
}
|
|
3020
3066
|
/**
|
|
3021
3067
|
* `finish-add-mfa`'s envelope construction, extracted pure so the outcome
|
|
@@ -4077,6 +4123,71 @@ let AuthWorkflow = class AuthWorkflow {
|
|
|
4077
4123
|
await this.users.unlockAccount(ctx.subject);
|
|
4078
4124
|
}
|
|
4079
4125
|
/**
|
|
4126
|
+
* The SINGLE login-completion funnel. Every flow that establishes an
|
|
4127
|
+
* authenticated session routes through here — placed in each login schema
|
|
4128
|
+
* right after the guards and BEFORE the delivery terminal (`issue` /
|
|
4129
|
+
* `mint-authz-code` / `finalize-auto-login`) — so a login can never be
|
|
4130
|
+
* delivered without passing this point. Two uniform jobs:
|
|
4131
|
+
* 1. Stamp `account.lastLogin` exactly once (the sole workflow writer of it).
|
|
4132
|
+
* 2. Fire the `afterLogin` customer hook.
|
|
4133
|
+
*
|
|
4134
|
+
* Idempotent per run via `ctx.loginRecorded`: the password `credentials` path
|
|
4135
|
+
* already stamped eagerly through `users.login()` and latched the flag, so the
|
|
4136
|
+
* stamp NO-OPS there (no double write) — but `afterLogin` still fires, so the
|
|
4137
|
+
* hook runs exactly once for password logins too. Federated (`sso-callback`)
|
|
4138
|
+
* and auto-login (invite/signup/recovery) paths never call `login()`, so this
|
|
4139
|
+
* is their sole stamp. Self-gates on `ctx.subject`. Runs AFTER
|
|
4140
|
+
* `prepare-semantic-flags` derived `isFirstLogin`, so a genuine first
|
|
4141
|
+
* federated login still observes `isFirstLogin === true`.
|
|
4142
|
+
*
|
|
4143
|
+
* A throw (from the stamp or the hook) aborts the flow BEFORE delivery, so the
|
|
4144
|
+
* login fails atomically — no half-issued session.
|
|
4145
|
+
*/
|
|
4146
|
+
recordLogin(ctx) {
|
|
4147
|
+
if (!ctx.subject) return void 0;
|
|
4148
|
+
if (!ctx.loginRecorded) {
|
|
4149
|
+
ctx.loginRecorded = true;
|
|
4150
|
+
return this.users.recordLogin(ctx.subject).then(() => this.afterLogin(ctx)).then(() => void 0);
|
|
4151
|
+
}
|
|
4152
|
+
const hook = this.afterLogin(ctx);
|
|
4153
|
+
return hook instanceof Promise ? hook.then(() => void 0) : void 0;
|
|
4154
|
+
}
|
|
4155
|
+
/**
|
|
4156
|
+
* Recovery-only guard, extracted from the finalize terminals so they stay pure
|
|
4157
|
+
* delivery. An `admin-only` lockout never self-unlocks, so a reset that left
|
|
4158
|
+
* the account frozen must NOT be confirmed-as-success OR auto-logged-in
|
|
4159
|
+
* (minting tokens would defeat the very freeze). When still locked it emits the
|
|
4160
|
+
* warn terminal and sets `ctx.aborted`, so the schema's following `{ break }`
|
|
4161
|
+
* skips BOTH the `record-login` funnel and the finalize terminals — a frozen
|
|
4162
|
+
* account is never stamped or logged in. The schema gates this on
|
|
4163
|
+
* `ctx.lockout?.mode === "admin-only"` (the only mode that can reach finalize
|
|
4164
|
+
* still locked: self-service ran `unlock-account`; temporary auto-expires).
|
|
4165
|
+
*/
|
|
4166
|
+
async recoveryLockCheck(ctx) {
|
|
4167
|
+
if (await this.recoveryLeftAccountLocked(ctx)) {
|
|
4168
|
+
this.finishRecoveryReset(ctx, true);
|
|
4169
|
+
ctx.aborted = true;
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
4172
|
+
/**
|
|
4173
|
+
* Thin dispatcher steps for the flow-specific lifecycle hooks. Each is its own
|
|
4174
|
+
* schema step (rather than an in-terminal call) because its flow's finalize
|
|
4175
|
+
* terminals are SHARED across flows (`finalize-auto-login` serves invite +
|
|
4176
|
+
* recovery + signup; `finalize-fresh-login` serves invite + recovery), so a
|
|
4177
|
+
* dedicated step in the flow-specific schema fires the right hook without
|
|
4178
|
+
* ctx-discrimination inside a shared terminal. Named `fire*` so the overridable
|
|
4179
|
+
* `after*` hooks keep the clean public name.
|
|
4180
|
+
*/
|
|
4181
|
+
fireInvitationAccepted(ctx) {
|
|
4182
|
+
return this.afterInvitationAccepted(ctx);
|
|
4183
|
+
}
|
|
4184
|
+
fireSignup(ctx) {
|
|
4185
|
+
return this.afterSignup(ctx);
|
|
4186
|
+
}
|
|
4187
|
+
firePasswordReset(ctx) {
|
|
4188
|
+
return this.afterPasswordReset(ctx);
|
|
4189
|
+
}
|
|
4190
|
+
/**
|
|
4080
4191
|
* Issue access + refresh tokens via `auth.issue`. Stashes the login
|
|
4081
4192
|
* response envelope on `useWfFinished` so downstream `redirect` can
|
|
4082
4193
|
* override with a redirect envelope while preserving the cookies.
|
|
@@ -4096,7 +4207,7 @@ let AuthWorkflow = class AuthWorkflow {
|
|
|
4096
4207
|
...cookies,
|
|
4097
4208
|
[name]: {
|
|
4098
4209
|
value,
|
|
4099
|
-
options: auth.cookieAttrs({ maxAge: ttlMs
|
|
4210
|
+
options: auth.cookieAttrs({ maxAge: ttlMs })
|
|
4100
4211
|
}
|
|
4101
4212
|
};
|
|
4102
4213
|
};
|
|
@@ -4308,9 +4419,6 @@ let AuthWorkflow = class AuthWorkflow {
|
|
|
4308
4419
|
(ctx.completion ??= {}).redirectUrl = target;
|
|
4309
4420
|
return;
|
|
4310
4421
|
}
|
|
4311
|
-
if (ctx.lockout?.mode === "admin-only") return this.recoveryLeftAccountLocked(ctx).then((locked) => {
|
|
4312
|
-
this.finishRecoveryReset(ctx, locked);
|
|
4313
|
-
});
|
|
4314
4422
|
this.finishRecoveryReset(ctx, false);
|
|
4315
4423
|
}
|
|
4316
4424
|
/**
|
|
@@ -4372,17 +4480,20 @@ let AuthWorkflow = class AuthWorkflow {
|
|
|
4372
4480
|
(ctx.completion ??= {}).redirectUrl = target;
|
|
4373
4481
|
}
|
|
4374
4482
|
/**
|
|
4375
|
-
* Auto-login finalize — invite + recovery
|
|
4376
|
-
* and stashes the login response envelope on
|
|
4377
|
-
* preserves any `message` set by an earlier terminal
|
|
4378
|
-
* the SPA paints the confirmation text alongside the
|
|
4483
|
+
* Auto-login finalize — invite + recovery + signup. PURE delivery: issues
|
|
4484
|
+
* access + refresh tokens and stashes the login response envelope on
|
|
4485
|
+
* `useWfFinished`. Invite preserves any `message` set by an earlier terminal
|
|
4486
|
+
* (`confirmation`) so the SPA paints the confirmation text alongside the
|
|
4487
|
+
* tokens (WF-INVITE-020).
|
|
4488
|
+
*
|
|
4489
|
+
* It records NOTHING and guards NOTHING: `account.lastLogin` + the
|
|
4490
|
+
* `afterLogin` hook are owned by the upstream `record-login` funnel step, and
|
|
4491
|
+
* the admin-only-survived-lock guard by the upstream `recovery-lock-check`
|
|
4492
|
+
* step — both of which `{ break }`/gate this step out when they apply, so a
|
|
4493
|
+
* still-frozen account never reaches here.
|
|
4379
4494
|
*/
|
|
4380
4495
|
async finalizeAutoLogin(ctx) {
|
|
4381
4496
|
this.requireSubject(ctx);
|
|
4382
|
-
if (ctx.lockout?.mode === "admin-only" && await this.recoveryLeftAccountLocked(ctx)) {
|
|
4383
|
-
this.finishRecoveryReset(ctx, true);
|
|
4384
|
-
return;
|
|
4385
|
-
}
|
|
4386
4497
|
const issue = await this.issueForContext(ctx);
|
|
4387
4498
|
const auth = useAuth();
|
|
4388
4499
|
const previousMessage = (useWfFinished().get()?.value)?.message;
|
|
@@ -5253,7 +5364,7 @@ __decorate([
|
|
|
5253
5364
|
__decorateParam(0, WorkflowParam("context")),
|
|
5254
5365
|
__decorateMetadata("design:type", Function),
|
|
5255
5366
|
__decorateMetadata("design:paramtypes", [Object]),
|
|
5256
|
-
__decorateMetadata("design:returntype",
|
|
5367
|
+
__decorateMetadata("design:returntype", Object)
|
|
5257
5368
|
], AuthWorkflow.prototype, "finishAddMfa", null);
|
|
5258
5369
|
__decorate([
|
|
5259
5370
|
Step("prepare-locked-mfa-transports"),
|
|
@@ -5511,6 +5622,46 @@ __decorate([
|
|
|
5511
5622
|
__decorateMetadata("design:paramtypes", [Object]),
|
|
5512
5623
|
__decorateMetadata("design:returntype", Promise)
|
|
5513
5624
|
], AuthWorkflow.prototype, "unlockAccount", null);
|
|
5625
|
+
__decorate([
|
|
5626
|
+
Step("record-login"),
|
|
5627
|
+
Public(),
|
|
5628
|
+
__decorateParam(0, WorkflowParam("context")),
|
|
5629
|
+
__decorateMetadata("design:type", Function),
|
|
5630
|
+
__decorateMetadata("design:paramtypes", [Object]),
|
|
5631
|
+
__decorateMetadata("design:returntype", Object)
|
|
5632
|
+
], AuthWorkflow.prototype, "recordLogin", null);
|
|
5633
|
+
__decorate([
|
|
5634
|
+
Step("recovery-lock-check"),
|
|
5635
|
+
Public(),
|
|
5636
|
+
__decorateParam(0, WorkflowParam("context")),
|
|
5637
|
+
__decorateMetadata("design:type", Function),
|
|
5638
|
+
__decorateMetadata("design:paramtypes", [Object]),
|
|
5639
|
+
__decorateMetadata("design:returntype", Promise)
|
|
5640
|
+
], AuthWorkflow.prototype, "recoveryLockCheck", null);
|
|
5641
|
+
__decorate([
|
|
5642
|
+
Step("after-invitation-accepted"),
|
|
5643
|
+
Public(),
|
|
5644
|
+
__decorateParam(0, WorkflowParam("context")),
|
|
5645
|
+
__decorateMetadata("design:type", Function),
|
|
5646
|
+
__decorateMetadata("design:paramtypes", [Object]),
|
|
5647
|
+
__decorateMetadata("design:returntype", Object)
|
|
5648
|
+
], AuthWorkflow.prototype, "fireInvitationAccepted", null);
|
|
5649
|
+
__decorate([
|
|
5650
|
+
Step("after-signup"),
|
|
5651
|
+
Public(),
|
|
5652
|
+
__decorateParam(0, WorkflowParam("context")),
|
|
5653
|
+
__decorateMetadata("design:type", Function),
|
|
5654
|
+
__decorateMetadata("design:paramtypes", [Object]),
|
|
5655
|
+
__decorateMetadata("design:returntype", Object)
|
|
5656
|
+
], AuthWorkflow.prototype, "fireSignup", null);
|
|
5657
|
+
__decorate([
|
|
5658
|
+
Step("after-password-reset"),
|
|
5659
|
+
Public(),
|
|
5660
|
+
__decorateParam(0, WorkflowParam("context")),
|
|
5661
|
+
__decorateMetadata("design:type", Function),
|
|
5662
|
+
__decorateMetadata("design:paramtypes", [Object]),
|
|
5663
|
+
__decorateMetadata("design:returntype", Object)
|
|
5664
|
+
], AuthWorkflow.prototype, "firePasswordReset", null);
|
|
5514
5665
|
__decorate([
|
|
5515
5666
|
Step("issue"),
|
|
5516
5667
|
Public(),
|
|
@@ -5557,7 +5708,7 @@ __decorate([
|
|
|
5557
5708
|
__decorateParam(0, WorkflowParam("context")),
|
|
5558
5709
|
__decorateMetadata("design:type", Function),
|
|
5559
5710
|
__decorateMetadata("design:paramtypes", [Object]),
|
|
5560
|
-
__decorateMetadata("design:returntype",
|
|
5711
|
+
__decorateMetadata("design:returntype", void 0)
|
|
5561
5712
|
], AuthWorkflow.prototype, "finalizeFreshLogin", null);
|
|
5562
5713
|
__decorate([
|
|
5563
5714
|
Step("finalize-auto-login"),
|
|
@@ -5723,6 +5874,10 @@ __decorate([
|
|
|
5723
5874
|
}]
|
|
5724
5875
|
},
|
|
5725
5876
|
{ break: (ctx) => !!ctx.aborted },
|
|
5877
|
+
{
|
|
5878
|
+
id: "record-login",
|
|
5879
|
+
condition: (ctx) => !!ctx.subject
|
|
5880
|
+
},
|
|
5726
5881
|
{
|
|
5727
5882
|
condition: (ctx) => !ctx.authz,
|
|
5728
5883
|
steps: [
|
|
@@ -5796,6 +5951,7 @@ __decorate([
|
|
|
5796
5951
|
...consentsPersistTailSchema,
|
|
5797
5952
|
{ id: "unset-pending-invitation" },
|
|
5798
5953
|
{ id: "activate-user" },
|
|
5954
|
+
{ id: "after-invitation-accepted" },
|
|
5799
5955
|
{
|
|
5800
5956
|
id: "confirmation",
|
|
5801
5957
|
condition: (ctx) => !!ctx.accept?.showConfirmation
|
|
@@ -5804,6 +5960,10 @@ __decorate([
|
|
|
5804
5960
|
id: "finalize-fresh-login",
|
|
5805
5961
|
condition: (ctx) => !ctx.autoLogin
|
|
5806
5962
|
},
|
|
5963
|
+
{
|
|
5964
|
+
id: "record-login",
|
|
5965
|
+
condition: (ctx) => !!ctx.autoLogin
|
|
5966
|
+
},
|
|
5807
5967
|
{
|
|
5808
5968
|
id: "finalize-auto-login",
|
|
5809
5969
|
condition: (ctx) => !!ctx.autoLogin
|
|
@@ -5846,10 +6006,20 @@ __decorate([
|
|
|
5846
6006
|
condition: (ctx) => ctx.lockout?.mode === "self-service"
|
|
5847
6007
|
},
|
|
5848
6008
|
...consentsPersistTailSchema,
|
|
6009
|
+
{ id: "after-password-reset" },
|
|
6010
|
+
{
|
|
6011
|
+
id: "recovery-lock-check",
|
|
6012
|
+
condition: (ctx) => ctx.lockout?.mode === "admin-only"
|
|
6013
|
+
},
|
|
6014
|
+
{ break: (ctx) => !!ctx.aborted },
|
|
5849
6015
|
{
|
|
5850
6016
|
id: "finalize-fresh-login",
|
|
5851
6017
|
condition: (ctx) => !ctx.autoLogin
|
|
5852
6018
|
},
|
|
6019
|
+
{
|
|
6020
|
+
id: "record-login",
|
|
6021
|
+
condition: (ctx) => !!ctx.autoLogin
|
|
6022
|
+
},
|
|
5853
6023
|
{
|
|
5854
6024
|
id: "finalize-auto-login",
|
|
5855
6025
|
condition: (ctx) => !!ctx.autoLogin
|
|
@@ -5946,6 +6116,8 @@ __decorate([
|
|
|
5946
6116
|
{ id: "activate-user" },
|
|
5947
6117
|
...consentsPersistTailSchema,
|
|
5948
6118
|
{ id: "signup-extra-step" },
|
|
6119
|
+
{ id: "after-signup" },
|
|
6120
|
+
{ id: "record-login" },
|
|
5949
6121
|
{ id: "finalize-auto-login" }
|
|
5950
6122
|
]),
|
|
5951
6123
|
__decorateMetadata("design:type", Function),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aooth/auth-moost",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
4
4
|
"description": "Moost auth integration for aoothjs — AuthGuard interceptor, useAuth composable, REST endpoints, workflows",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"aoothjs",
|
|
@@ -57,27 +57,27 @@
|
|
|
57
57
|
"access": "public"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@atscript/moost-wf": "^0.1.
|
|
60
|
+
"@atscript/moost-wf": "^0.1.102",
|
|
61
61
|
"@wooksjs/http-body": "^0.7.19",
|
|
62
|
-
"@aooth/arbac-moost": "^0.1.
|
|
63
|
-
"@aooth/
|
|
64
|
-
"@aooth/
|
|
65
|
-
"@aooth/user": "0.1.
|
|
62
|
+
"@aooth/arbac-moost": "^0.1.25",
|
|
63
|
+
"@aooth/auth": "0.1.25",
|
|
64
|
+
"@aooth/idp": "0.1.25",
|
|
65
|
+
"@aooth/user": "0.1.25"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@atscript/core": "^0.1.
|
|
69
|
-
"@atscript/typescript": "^0.1.
|
|
70
|
-
"@atscript/ui": "^0.1.
|
|
71
|
-
"@atscript/ui-fns": "^0.1.
|
|
68
|
+
"@atscript/core": "^0.1.77",
|
|
69
|
+
"@atscript/typescript": "^0.1.77",
|
|
70
|
+
"@atscript/ui": "^0.1.102",
|
|
71
|
+
"@atscript/ui-fns": "^0.1.102",
|
|
72
72
|
"@moostjs/event-http": "^0.6.27",
|
|
73
73
|
"@moostjs/event-wf": "^0.6.27",
|
|
74
74
|
"moost": "^0.6.27",
|
|
75
|
-
"unplugin-atscript": "^0.1.
|
|
75
|
+
"unplugin-atscript": "^0.1.77",
|
|
76
76
|
"wooks": "^0.7.19"
|
|
77
77
|
},
|
|
78
78
|
"peerDependencies": {
|
|
79
|
-
"@atscript/moost-wf": "^0.1.
|
|
80
|
-
"@atscript/typescript": "^0.1.
|
|
79
|
+
"@atscript/moost-wf": "^0.1.102",
|
|
80
|
+
"@atscript/typescript": "^0.1.77",
|
|
81
81
|
"@moostjs/event-http": "^0.6.27",
|
|
82
82
|
"@moostjs/event-wf": "^0.6.27",
|
|
83
83
|
"@wooksjs/event-core": "^0.7.19",
|