@genesislcap/foundation-auth 15.14.2-FUI-2603.1 → 15.14.2-FUI-2603.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/dts/machine/dto.d.ts +34 -0
- package/dist/dts/machine/dto.d.ts.map +1 -0
- package/dist/dts/machine/dto.test.d.ts +2 -0
- package/dist/dts/machine/dto.test.d.ts.map +1 -0
- package/dist/dts/machine/machine.d.ts.map +1 -1
- package/dist/dts/routes/base.d.ts +5 -0
- package/dist/dts/routes/base.d.ts.map +1 -1
- package/dist/esm/machine/dto.js +76 -0
- package/dist/esm/machine/dto.test.js +88 -0
- package/dist/esm/machine/machine.js +70 -35
- package/dist/esm/routes/base.js +15 -1
- package/package.json +19 -19
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { EventObject } from 'xstate';
|
|
2
|
+
import type { CredentialData } from '../credential';
|
|
3
|
+
/**
|
|
4
|
+
* Unwrap a Genesis auth DTO from FetchMachine output or APIError.
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
export declare function unwrapAuthDto(value: unknown): unknown;
|
|
8
|
+
type AuthMachineEventLike = EventObject & {
|
|
9
|
+
data?: unknown;
|
|
10
|
+
error?: unknown;
|
|
11
|
+
output?: unknown;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Read the auth DTO from a fetch done/error event.
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
export declare function authDtoFromMachineEvent(event: AuthMachineEventLike): unknown;
|
|
18
|
+
/**
|
|
19
|
+
* True when a fetch done/error event carries a Genesis NACK / ERROR payload.
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
export declare function isAuthNackEvent(event: AuthMachineEventLike): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* True when fetch completed in the failed final state without a Genesis NACK body.
|
|
25
|
+
* @public
|
|
26
|
+
*/
|
|
27
|
+
export declare function isFetchFailureOutput(event: AuthMachineEventLike): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Fire-and-forget credential persistence so xstate does not hold pending on the Promise.
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
export declare function persistCredentials(store: (data: CredentialData) => Promise<unknown> | unknown, data: CredentialData): void;
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=dto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dto.d.ts","sourceRoot":"","sources":["../../../src/machine/dto.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAMpD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAcrD;AAED,KAAK,oBAAoB,GAAG,WAAW,GAAG;IACxC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAMF;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAQ5E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAEpE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CASzE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,EAC3D,IAAI,EAAE,cAAc,GACnB,IAAI,CASN"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dto.test.d.ts","sourceRoot":"","sources":["../../../src/machine/dto.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../../../src/machine/machine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EAEf,YAAY,EACZ,iBAAiB,
|
|
1
|
+
{"version":3,"file":"machine.d.ts","sourceRoot":"","sources":["../../../src/machine/machine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EAEf,YAAY,EACZ,iBAAiB,EAKjB,OAAO,EACR,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AACpD,OAAO,EACL,IAAI,EAQL,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAEjD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAEL,iBAAiB,EAIlB,MAAM,eAAe,CAAC;AAIvB,OAAO,EACL,iBAAiB,EAQlB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAShD,OAAO,KAAK,EAEV,kBAAkB,EAClB,gBAAgB,EAGhB,WAAW,EACX,WAAW,EAEZ,MAAM,SAAS,CAAC;AAGjB;;;GAGG;AACH,MAAM,WAAW,WAAY,SAAQ,OAAO,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;CAAG;AAErF;;;GAGG;AACH,qBAAa,kBACX,SAAQ,eAAe,CAAC,kBAAkB,EAAE,gBAAgB,CAC5D,YAAW,WAAW;IAGd,OAAO,CAAC,IAAI;IACJ,OAAO,CAAC,YAAY;IACf,OAAO,CAAC,EAAE;IACvB,OAAO,CAAC,IAAI;IACC,OAAO,CAAC,aAAa;IAC5B,OAAO,CAAC,MAAM;IACb,OAAO,CAAC,OAAO;gBANd,IAAI,EAAE,IAAI,EACF,YAAY,EAAE,YAAY,EACrB,EAAE,EAAE,iBAAiB,EAClC,IAAI,EAAE,IAAI,EACG,aAAa,EAAE,iBAAiB,EACvC,MAAM,EAAE,UAAU,EACjB,OAAO,EAAE,WAAW;IA4sB3C,KAAK,CACH,OAAO,GAAE,kBAAkB,CAAC,OAAO,IAAI,CAAC,OAAO,CAM9C;IAKH;;OAEG;IACH,gBAAgB,IAAI,IAAI;IAIxB,SAAS,CAAC,sBAAsB,IAAI,WAAW;IAQ/C,SAAS,CAAC,+BAA+B,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW;IAuB3E,SAAS,CAAC,uBAAuB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK,GAAG,iBAAiB;IAgB3E,SAAS,CAAC,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;MA4BpB;IAEF,SAAS,CAAC,aAAa;;;MAGpB;CACJ;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,mEAA0E,CAAC;AAEnG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAE5C"}
|
|
@@ -405,6 +405,11 @@ export declare class BaseRoute extends ConfigHostElement {
|
|
|
405
405
|
store: AuthStore;
|
|
406
406
|
routing: AuthRouting;
|
|
407
407
|
connectedCallback(): void;
|
|
408
|
+
/**
|
|
409
|
+
* Skip dismiss while a request or MFA challenge is in flight so route
|
|
410
|
+
* (re)connect does not cancel the current auth step.
|
|
411
|
+
*/
|
|
412
|
+
protected get shouldDismissOnConnect(): boolean;
|
|
408
413
|
onBack: () => void;
|
|
409
414
|
toLocalisedText(text: string): string;
|
|
410
415
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/routes/base.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAGpD,OAAO,EAEL,OAAO,EAQR,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAGvD,OAAO,EAAE,UAAU,EAAqB,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC;;GAEG;AACH,KAAK,QAAQ,GAAG,YAAY,GAAG,qBAAqB,GAAG,EAAE,CAAC;AAE1D;;GAEG;AACH,eAAO,MAAM,SAAS,yKAAiC,CAAC;AAExD;;GAEG;AACH,eAAO,MAAM,UAAU,yKAAkC,CAAC;AAE1D;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,UAAU,CAAC;CACpB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEF;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,sBAAoC,YAAW,UAAU;IACnF,SAAS,EAAE,SAAS,CAAC;IACpB,MAAM,EAAE,UAAU,CAAC;IAC/B;;;OAGG;IACM,OAAO,EAAG,OAAO,CAAC;IAE3B,iBAAiB;CAYlB;AAED;;;;GAIG;AACH,qBAAa,SAAU,SAAQ,iBAAiB;IACxC,IAAI,EAAE,IAAI,CAAC;IACN,KAAK,EAAE,SAAS,CAAC;IACf,OAAO,EAAE,WAAW,CAAC;IAElC,iBAAiB;
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/routes/base.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAC;AAGpD,OAAO,EAEL,OAAO,EAQR,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAGvD,OAAO,EAAE,UAAU,EAAqB,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC;;GAEG;AACH,KAAK,QAAQ,GAAG,YAAY,GAAG,qBAAqB,GAAG,EAAE,CAAC;AAE1D;;GAEG;AACH,eAAO,MAAM,SAAS,yKAAiC,CAAC;AAExD;;GAEG;AACH,eAAO,MAAM,UAAU,yKAAkC,CAAC;AAE1D;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,UAAU,CAAC;CACpB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEF;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,sBAAoC,YAAW,UAAU;IACnF,SAAS,EAAE,SAAS,CAAC;IACpB,MAAM,EAAE,UAAU,CAAC;IAC/B;;;OAGG;IACM,OAAO,EAAG,OAAO,CAAC;IAE3B,iBAAiB;CAYlB;AAED;;;;GAIG;AACH,qBAAa,SAAU,SAAQ,iBAAiB;IACxC,IAAI,EAAE,IAAI,CAAC;IACN,KAAK,EAAE,SAAS,CAAC;IACf,OAAO,EAAE,WAAW,CAAC;IAElC,iBAAiB;IAQjB;;;OAGG;IACH,SAAS,KAAK,sBAAsB,IAAI,OAAO,CAM9C;IAED,MAAM,aAAsC;IAE5C,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAGtC;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,SAAS,6DAsBpD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,SAAS,SAAS,6DAmBhD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,SAAS,6DAoB7C,CAAC;AAoBF;;GAEG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,SAAS,SAAS,EAAE,QAAQ,MAAM,2DAgBhE,CAAC;AAyBF;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,SAAS,EAChD,MAAM,KAAK,GAAG,cAAc,GAAG,YAAY,2DAkB5C,CAAC;AAoHF;;GAEG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,UAAU,EAAE,SAAS,OAAO,EAAE,OAAO,MAAM,2DAU/E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,UAAU,EAC/C,OAAO,MAAM,EACb,aAAY,MAAiB,EAC7B,WAAU,OAAe,2DAW1B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,wBAAwB,GAAI,CAAC,SAAS,SAAS,6DAO3D,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { isAPIError, isDoneInvokeEvent, isErrorCustomEvent, isErrorEvent, } from '@genesislcap/foundation-state-machine';
|
|
2
|
+
import { isFailedAuthDTO } from '../mapper/utils';
|
|
3
|
+
const hasMessageType = (value) => typeof value === 'object' && value !== null && 'MESSAGE_TYPE' in value;
|
|
4
|
+
/**
|
|
5
|
+
* Unwrap a Genesis auth DTO from FetchMachine output or APIError.
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
export function unwrapAuthDto(value) {
|
|
9
|
+
if (value == null) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
if (isAPIError(value)) {
|
|
13
|
+
return unwrapAuthDto(value.data);
|
|
14
|
+
}
|
|
15
|
+
if (hasMessageType(value)) {
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
if (typeof value === 'object' && 'data' in value) {
|
|
19
|
+
return unwrapAuthDto(value.data);
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
function eventErrorPayload(event) {
|
|
24
|
+
var _a, _b;
|
|
25
|
+
return (_b = (_a = event.data) !== null && _a !== void 0 ? _a : event.error) !== null && _b !== void 0 ? _b : event.output;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Read the auth DTO from a fetch done/error event.
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
export function authDtoFromMachineEvent(event) {
|
|
32
|
+
var _a;
|
|
33
|
+
if (isDoneInvokeEvent(event)) {
|
|
34
|
+
return unwrapAuthDto(event.output);
|
|
35
|
+
}
|
|
36
|
+
if (isErrorEvent(event) || isErrorCustomEvent(event)) {
|
|
37
|
+
return unwrapAuthDto(eventErrorPayload(event));
|
|
38
|
+
}
|
|
39
|
+
return unwrapAuthDto((_a = eventErrorPayload(event)) !== null && _a !== void 0 ? _a : event);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* True when a fetch done/error event carries a Genesis NACK / ERROR payload.
|
|
43
|
+
* @public
|
|
44
|
+
*/
|
|
45
|
+
export function isAuthNackEvent(event) {
|
|
46
|
+
return isFailedAuthDTO(authDtoFromMachineEvent(event));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* True when fetch completed in the failed final state without a Genesis NACK body.
|
|
50
|
+
* @public
|
|
51
|
+
*/
|
|
52
|
+
export function isFetchFailureOutput(event) {
|
|
53
|
+
if (!isDoneInvokeEvent(event)) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
const output = event.output;
|
|
57
|
+
if ((output === null || output === void 0 ? void 0 : output.error) == null) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
return !isAuthNackEvent(event);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Fire-and-forget credential persistence so xstate does not hold pending on the Promise.
|
|
64
|
+
* @public
|
|
65
|
+
*/
|
|
66
|
+
export function persistCredentials(store, data) {
|
|
67
|
+
try {
|
|
68
|
+
const result = store(data);
|
|
69
|
+
if (result && typeof result.then === 'function') {
|
|
70
|
+
void result.catch(() => undefined);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (_a) {
|
|
74
|
+
// Credential storage is best-effort.
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { __awaiter } from "tslib";
|
|
2
|
+
import { APIError } from '@genesislcap/foundation-state-machine';
|
|
3
|
+
import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
|
|
4
|
+
import { StatusCodes } from 'http-status-codes';
|
|
5
|
+
import { authDtoFromMachineEvent, isAuthNackEvent, isFetchFailureOutput, persistCredentials, unwrapAuthDto, } from './dto';
|
|
6
|
+
const Suite = createLogicSuite('authDtoFromMachineEvent');
|
|
7
|
+
const mfaNack = {
|
|
8
|
+
ERROR: [
|
|
9
|
+
{
|
|
10
|
+
'@type': 'LoginError',
|
|
11
|
+
CODE: 'MFA_REQUIRED',
|
|
12
|
+
TEXT: 'Problem logging in',
|
|
13
|
+
STATUS_CODE: '403 Forbidden',
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
WARNING: [],
|
|
17
|
+
MESSAGE_TYPE: 'EVENT_LOGIN_AUTH_NACK',
|
|
18
|
+
SOURCE_REF: 'ref',
|
|
19
|
+
};
|
|
20
|
+
const eventAck = {
|
|
21
|
+
MESSAGE_TYPE: 'EVENT_ACK',
|
|
22
|
+
SOURCE_REF: 'ack-ref',
|
|
23
|
+
};
|
|
24
|
+
Suite('unwraps DTO from FetchMachineOutput', () => {
|
|
25
|
+
const dto = unwrapAuthDto({ data: mfaNack, response: { ok: false } });
|
|
26
|
+
assert.is(dto.MESSAGE_TYPE, 'EVENT_LOGIN_AUTH_NACK');
|
|
27
|
+
});
|
|
28
|
+
Suite('unwraps DTO from APIError', () => {
|
|
29
|
+
const dto = unwrapAuthDto(new APIError(StatusCodes.FORBIDDEN, mfaNack));
|
|
30
|
+
assert.is(dto.MESSAGE_TYPE, 'EVENT_LOGIN_AUTH_NACK');
|
|
31
|
+
});
|
|
32
|
+
Suite('treats HTTP 403 MFA NACK as auth nack on error.platform', () => {
|
|
33
|
+
assert.ok(isAuthNackEvent({
|
|
34
|
+
type: 'error.platform.loginFetcher',
|
|
35
|
+
data: new APIError(StatusCodes.FORBIDDEN, mfaNack),
|
|
36
|
+
}));
|
|
37
|
+
});
|
|
38
|
+
Suite('treats HTTP 403 MFA NACK as auth nack on done.invoke wrapping APIError', () => {
|
|
39
|
+
assert.ok(isAuthNackEvent({
|
|
40
|
+
type: 'done.invoke.loginFetcher',
|
|
41
|
+
output: new APIError(StatusCodes.FORBIDDEN, mfaNack),
|
|
42
|
+
}));
|
|
43
|
+
});
|
|
44
|
+
Suite('does not treat EVENT_ACK as nack', () => {
|
|
45
|
+
assert.not.ok(isAuthNackEvent({
|
|
46
|
+
type: 'done.invoke.resetPasswordFetcher',
|
|
47
|
+
output: { data: eventAck, response: { ok: true } },
|
|
48
|
+
}));
|
|
49
|
+
assert.is(authDtoFromMachineEvent({
|
|
50
|
+
type: 'done.invoke.resetPasswordFetcher',
|
|
51
|
+
output: { data: eventAck, response: { ok: true } },
|
|
52
|
+
}).MESSAGE_TYPE, 'EVENT_ACK');
|
|
53
|
+
});
|
|
54
|
+
Suite('unwraps DTO from xstate error payload on error field', () => {
|
|
55
|
+
assert.ok(isAuthNackEvent({
|
|
56
|
+
type: 'xstate.error.actor.0',
|
|
57
|
+
error: new APIError(StatusCodes.FORBIDDEN, mfaNack),
|
|
58
|
+
}));
|
|
59
|
+
});
|
|
60
|
+
Suite('does not treat HTTP 403 MFA NACK as a generic fetch failure', () => {
|
|
61
|
+
assert.not.ok(isFetchFailureOutput({
|
|
62
|
+
type: 'done.invoke.loginFetcher',
|
|
63
|
+
output: {
|
|
64
|
+
data: mfaNack,
|
|
65
|
+
error: new APIError(StatusCodes.FORBIDDEN, mfaNack),
|
|
66
|
+
},
|
|
67
|
+
}));
|
|
68
|
+
});
|
|
69
|
+
Suite('treats network/API failure without NACK as fetch failure', () => {
|
|
70
|
+
assert.ok(isFetchFailureOutput({
|
|
71
|
+
type: 'done.invoke.loginFetcher',
|
|
72
|
+
output: {
|
|
73
|
+
data: undefined,
|
|
74
|
+
error: new APIError(StatusCodes.INTERNAL_SERVER_ERROR),
|
|
75
|
+
},
|
|
76
|
+
}));
|
|
77
|
+
});
|
|
78
|
+
Suite('persistCredentials does not return the storage Promise', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
79
|
+
let stored;
|
|
80
|
+
const result = persistCredentials((data) => new Promise((resolve) => {
|
|
81
|
+
stored = data;
|
|
82
|
+
resolve(true);
|
|
83
|
+
}), { id: 'jane', name: 'jane', password: 'x' });
|
|
84
|
+
assert.is(result, undefined);
|
|
85
|
+
yield Promise.resolve();
|
|
86
|
+
assert.is(stored === null || stored === void 0 ? void 0 : stored.id, 'jane');
|
|
87
|
+
}));
|
|
88
|
+
Suite.run();
|
|
@@ -12,6 +12,7 @@ import { clearResetIdentity } from '../routes/reset-password/reset-identity';
|
|
|
12
12
|
import { AuthRouting } from '../routes/routing';
|
|
13
13
|
import { logger } from '../utils';
|
|
14
14
|
import { clearAuthErrorState, setError } from './actions';
|
|
15
|
+
import { authDtoFromMachineEvent, isAuthNackEvent as eventHasAuthNack, isFetchFailureOutput, persistCredentials, } from './dto';
|
|
15
16
|
import { defaultAuthMachineContext, isLoginParams, isQueryParams } from './types';
|
|
16
17
|
/**
|
|
17
18
|
* Default AuthMachine.
|
|
@@ -127,7 +128,17 @@ let DefaultAuthMachine = class DefaultAuthMachine extends AbstractMachine {
|
|
|
127
128
|
invoke: {
|
|
128
129
|
id: 'loginDetailsFetcher',
|
|
129
130
|
src: fetchMachine.machine,
|
|
130
|
-
onDone:
|
|
131
|
+
onDone: [
|
|
132
|
+
{
|
|
133
|
+
guard: 'isUnauthorizedError',
|
|
134
|
+
target: 'loggedOut',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
guard: 'isFetchFailureOutput',
|
|
138
|
+
target: 'error.silentLogin',
|
|
139
|
+
},
|
|
140
|
+
this.dtoTransition(),
|
|
141
|
+
],
|
|
131
142
|
onError: [
|
|
132
143
|
{
|
|
133
144
|
guard: 'isUnauthorizedError',
|
|
@@ -151,8 +162,23 @@ let DefaultAuthMachine = class DefaultAuthMachine extends AbstractMachine {
|
|
|
151
162
|
invoke: {
|
|
152
163
|
id: 'loginFetcher',
|
|
153
164
|
src: fetchMachine.machine,
|
|
154
|
-
onDone:
|
|
155
|
-
|
|
165
|
+
onDone: [
|
|
166
|
+
{
|
|
167
|
+
guard: 'isFetchFailureOutput',
|
|
168
|
+
target: 'error.login',
|
|
169
|
+
},
|
|
170
|
+
this.dtoTransition(),
|
|
171
|
+
],
|
|
172
|
+
onError: [
|
|
173
|
+
{
|
|
174
|
+
guard: 'isAuthNackEvent',
|
|
175
|
+
actions: ['setDTO'],
|
|
176
|
+
target: 'mapEntity',
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
target: 'error.login',
|
|
180
|
+
},
|
|
181
|
+
],
|
|
156
182
|
},
|
|
157
183
|
entry: [
|
|
158
184
|
sendTo('loginFetcher', ({ context }) => {
|
|
@@ -210,7 +236,13 @@ let DefaultAuthMachine = class DefaultAuthMachine extends AbstractMachine {
|
|
|
210
236
|
invoke: {
|
|
211
237
|
id: 'logoutFetcher',
|
|
212
238
|
src: fetchMachine.machine,
|
|
213
|
-
onDone:
|
|
239
|
+
onDone: [
|
|
240
|
+
{
|
|
241
|
+
guard: 'isFetchFailureOutput',
|
|
242
|
+
target: 'error.logout',
|
|
243
|
+
},
|
|
244
|
+
this.dtoTransition(),
|
|
245
|
+
],
|
|
214
246
|
onError: 'error.logout',
|
|
215
247
|
},
|
|
216
248
|
entry: [
|
|
@@ -246,6 +278,10 @@ let DefaultAuthMachine = class DefaultAuthMachine extends AbstractMachine {
|
|
|
246
278
|
guard: 'is200NackDTO',
|
|
247
279
|
target: 'error.forgotPassword',
|
|
248
280
|
},
|
|
281
|
+
{
|
|
282
|
+
guard: 'isFetchFailureOutput',
|
|
283
|
+
target: 'error.forgotPassword',
|
|
284
|
+
},
|
|
249
285
|
{
|
|
250
286
|
target: 'feedback.forgotPassword',
|
|
251
287
|
},
|
|
@@ -274,11 +310,15 @@ let DefaultAuthMachine = class DefaultAuthMachine extends AbstractMachine {
|
|
|
274
310
|
guard: 'is200NackDTO',
|
|
275
311
|
target: 'error.changePassword',
|
|
276
312
|
},
|
|
313
|
+
{
|
|
314
|
+
guard: 'isFetchFailureOutput',
|
|
315
|
+
target: 'error.changePassword',
|
|
316
|
+
},
|
|
277
317
|
{
|
|
278
318
|
actions: [
|
|
279
319
|
({ context }) => {
|
|
280
320
|
const params = context.params;
|
|
281
|
-
this.cm.storeCredentials({
|
|
321
|
+
persistCredentials(this.cm.storeCredentials.bind(this.cm), {
|
|
282
322
|
id: params.username,
|
|
283
323
|
name: params.username,
|
|
284
324
|
password: params.newPassword,
|
|
@@ -312,11 +352,15 @@ let DefaultAuthMachine = class DefaultAuthMachine extends AbstractMachine {
|
|
|
312
352
|
guard: 'is200NackDTO',
|
|
313
353
|
target: 'error.resetPassword',
|
|
314
354
|
},
|
|
355
|
+
{
|
|
356
|
+
guard: 'isFetchFailureOutput',
|
|
357
|
+
target: 'error.resetPassword',
|
|
358
|
+
},
|
|
315
359
|
{
|
|
316
360
|
actions: [
|
|
317
361
|
({ context }) => {
|
|
318
362
|
const params = context.params;
|
|
319
|
-
this.cm.storeCredentials({
|
|
363
|
+
persistCredentials(this.cm.storeCredentials.bind(this.cm), {
|
|
320
364
|
id: params.username,
|
|
321
365
|
name: params.username,
|
|
322
366
|
password: params.newPassword,
|
|
@@ -469,17 +513,12 @@ let DefaultAuthMachine = class DefaultAuthMachine extends AbstractMachine {
|
|
|
469
513
|
],
|
|
470
514
|
},
|
|
471
515
|
/**
|
|
472
|
-
* Feedback states.
|
|
473
|
-
*
|
|
516
|
+
* Feedback states. Success paths auto-return after feedbackDelay.
|
|
517
|
+
* Error paths stay put so the user can retry immediately (login is handled here).
|
|
474
518
|
*/
|
|
475
519
|
feedback: {
|
|
476
520
|
initial: 'none',
|
|
477
|
-
on:
|
|
478
|
-
dismiss: {
|
|
479
|
-
actions: ['resetContext'],
|
|
480
|
-
target: '#authMachine.idle',
|
|
481
|
-
},
|
|
482
|
-
},
|
|
521
|
+
on: this.onIdleEvents(),
|
|
483
522
|
states: {
|
|
484
523
|
none: {
|
|
485
524
|
tags: ['feedback', 'none'],
|
|
@@ -522,17 +561,6 @@ let DefaultAuthMachine = class DefaultAuthMachine extends AbstractMachine {
|
|
|
522
561
|
meta: {
|
|
523
562
|
content: 'MESSAGE_INCORRECT_CREDENTIALS',
|
|
524
563
|
},
|
|
525
|
-
after: {
|
|
526
|
-
feedbackDelay: [
|
|
527
|
-
{
|
|
528
|
-
guard: 'isMfaLoginParams',
|
|
529
|
-
target: '#authMachine.awaitingMfa',
|
|
530
|
-
},
|
|
531
|
-
{
|
|
532
|
-
target: '#authMachine.idle',
|
|
533
|
-
},
|
|
534
|
-
],
|
|
535
|
-
},
|
|
536
564
|
},
|
|
537
565
|
mfaRequired: {
|
|
538
566
|
tags: ['feedback', 'mfaRequired'],
|
|
@@ -607,9 +635,10 @@ let DefaultAuthMachine = class DefaultAuthMachine extends AbstractMachine {
|
|
|
607
635
|
params: ({ context }) => this.convertQueryParamsToLoginParams(context.params),
|
|
608
636
|
}),
|
|
609
637
|
setDTO: assign(({ event }) => {
|
|
610
|
-
|
|
638
|
+
const dto = authDtoFromMachineEvent(event);
|
|
639
|
+
if (dto) {
|
|
611
640
|
return {
|
|
612
|
-
dto
|
|
641
|
+
dto,
|
|
613
642
|
};
|
|
614
643
|
}
|
|
615
644
|
}),
|
|
@@ -657,7 +686,7 @@ let DefaultAuthMachine = class DefaultAuthMachine extends AbstractMachine {
|
|
|
657
686
|
password: loginParams.password,
|
|
658
687
|
};
|
|
659
688
|
if (this.cm.validateUserCredentialData(data, true)) {
|
|
660
|
-
this.cm.storeCredentials(data);
|
|
689
|
+
persistCredentials(this.cm.storeCredentials.bind(this.cm), data);
|
|
661
690
|
}
|
|
662
691
|
},
|
|
663
692
|
postLoginRedirect: this.config.postLoginRedirect,
|
|
@@ -678,7 +707,16 @@ let DefaultAuthMachine = class DefaultAuthMachine extends AbstractMachine {
|
|
|
678
707
|
hasSSOToken: ({ context }) => isQueryParams(context.params) && context.params.SSO_TOKEN !== undefined,
|
|
679
708
|
hasMFACode: ({ context }) => isQueryParams(context.params) && context.params.MFA_CODE !== undefined,
|
|
680
709
|
hasWindowOpener: () => window.opener,
|
|
681
|
-
isUnauthorizedError: ({ event }) =>
|
|
710
|
+
isUnauthorizedError: ({ event }) => {
|
|
711
|
+
if (isErrorEvent(event)) {
|
|
712
|
+
return isUnauthorizedError(event.data);
|
|
713
|
+
}
|
|
714
|
+
if (isDoneInvokeEvent(event)) {
|
|
715
|
+
const output = event.output;
|
|
716
|
+
return (output === null || output === void 0 ? void 0 : output.error) ? isUnauthorizedError(output.error) : false;
|
|
717
|
+
}
|
|
718
|
+
return false;
|
|
719
|
+
},
|
|
682
720
|
isAuthenticated: ({ context }) => { var _a; return (_a = context.user) === null || _a === void 0 ? void 0 : _a.isAuthenticated; },
|
|
683
721
|
isBanned: ({ context }) => { var _a; return (_a = context.user) === null || _a === void 0 ? void 0 : _a.isBanned(); },
|
|
684
722
|
isLoginAckEntity: ({ event }) => isDoneInvokeEvent(event) ? isLoginAckEntity(event.output) : false,
|
|
@@ -703,12 +741,9 @@ let DefaultAuthMachine = class DefaultAuthMachine extends AbstractMachine {
|
|
|
703
741
|
* Required until strict http status codes are enabled on the backend by default.
|
|
704
742
|
* Treats EVENT_NACK / ERROR payloads as failure regardless of HTTP status.
|
|
705
743
|
*/
|
|
706
|
-
is200NackDTO: ({ event }) =>
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
? isFailedAuthDTO((_a = event.output) === null || _a === void 0 ? void 0 : _a.data)
|
|
710
|
-
: false;
|
|
711
|
-
},
|
|
744
|
+
is200NackDTO: ({ event }) => isFailedAuthDTO(authDtoFromMachineEvent(event)),
|
|
745
|
+
isAuthNackEvent: ({ event }) => eventHasAuthNack(event),
|
|
746
|
+
isFetchFailureOutput: ({ event }) => isFetchFailureOutput(event),
|
|
712
747
|
},
|
|
713
748
|
delays: {
|
|
714
749
|
feedbackDelay: this.config.feedbackDelay,
|
package/dist/esm/routes/base.js
CHANGED
|
@@ -57,7 +57,21 @@ export class BaseRoute extends ConfigHostElement {
|
|
|
57
57
|
var _a, _b, _c, _d, _e;
|
|
58
58
|
super.connectedCallback();
|
|
59
59
|
(_c = (_b = (_a = this.store) === null || _a === void 0 ? void 0 : _a.formEntry) === null || _b === void 0 ? void 0 : _b.errors) === null || _c === void 0 ? void 0 : _c.clear();
|
|
60
|
-
|
|
60
|
+
if (this.shouldDismissOnConnect) {
|
|
61
|
+
(_e = (_d = this.store) === null || _d === void 0 ? void 0 : _d.authMachine) === null || _e === void 0 ? void 0 : _e.send({ type: 'dismiss' });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Skip dismiss while a request or MFA challenge is in flight so route
|
|
66
|
+
* (re)connect does not cancel the current auth step.
|
|
67
|
+
*/
|
|
68
|
+
get shouldDismissOnConnect() {
|
|
69
|
+
var _a;
|
|
70
|
+
const machine = (_a = this.store) === null || _a === void 0 ? void 0 : _a.authMachine;
|
|
71
|
+
if (!machine) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
return !machine.hasTag('pending') && !machine.hasTag('awaitingMfa');
|
|
61
75
|
}
|
|
62
76
|
toLocalisedText(text) {
|
|
63
77
|
return this.i18next.t(`${defaultAuthConfig.name}:${text}`, { lng: this.i18next.language });
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genesislcap/foundation-auth",
|
|
3
3
|
"description": "Genesis Foundation Auth",
|
|
4
|
-
"version": "15.14.2-FUI-2603.
|
|
4
|
+
"version": "15.14.2-FUI-2603.2",
|
|
5
5
|
"license": "SEE LICENSE IN license.txt",
|
|
6
6
|
"main": "dist/esm/index.js",
|
|
7
7
|
"types": "dist/foundation-auth.d.ts",
|
|
@@ -134,25 +134,25 @@
|
|
|
134
134
|
}
|
|
135
135
|
},
|
|
136
136
|
"devDependencies": {
|
|
137
|
-
"@genesislcap/foundation-testing": "15.14.2-FUI-2603.
|
|
138
|
-
"@genesislcap/genx": "15.14.2-FUI-2603.
|
|
139
|
-
"@genesislcap/rollup-builder": "15.14.2-FUI-2603.
|
|
140
|
-
"@genesislcap/ts-builder": "15.14.2-FUI-2603.
|
|
141
|
-
"@genesislcap/uvu-playwright-builder": "15.14.2-FUI-2603.
|
|
142
|
-
"@genesislcap/vite-builder": "15.14.2-FUI-2603.
|
|
143
|
-
"@genesislcap/webpack-builder": "15.14.2-FUI-2603.
|
|
137
|
+
"@genesislcap/foundation-testing": "15.14.2-FUI-2603.2",
|
|
138
|
+
"@genesislcap/genx": "15.14.2-FUI-2603.2",
|
|
139
|
+
"@genesislcap/rollup-builder": "15.14.2-FUI-2603.2",
|
|
140
|
+
"@genesislcap/ts-builder": "15.14.2-FUI-2603.2",
|
|
141
|
+
"@genesislcap/uvu-playwright-builder": "15.14.2-FUI-2603.2",
|
|
142
|
+
"@genesislcap/vite-builder": "15.14.2-FUI-2603.2",
|
|
143
|
+
"@genesislcap/webpack-builder": "15.14.2-FUI-2603.2"
|
|
144
144
|
},
|
|
145
145
|
"dependencies": {
|
|
146
|
-
"@genesislcap/foundation-comms": "15.14.2-FUI-2603.
|
|
147
|
-
"@genesislcap/foundation-events": "15.14.2-FUI-2603.
|
|
148
|
-
"@genesislcap/foundation-i18n": "15.14.2-FUI-2603.
|
|
149
|
-
"@genesislcap/foundation-logger": "15.14.2-FUI-2603.
|
|
150
|
-
"@genesislcap/foundation-state-machine": "15.14.2-FUI-2603.
|
|
151
|
-
"@genesislcap/foundation-store": "15.14.2-FUI-2603.
|
|
152
|
-
"@genesislcap/foundation-ui": "15.14.2-FUI-2603.
|
|
153
|
-
"@genesislcap/foundation-user": "15.14.2-FUI-2603.
|
|
154
|
-
"@genesislcap/foundation-utils": "15.14.2-FUI-2603.
|
|
155
|
-
"@genesislcap/foundation-zero": "15.14.2-FUI-2603.
|
|
146
|
+
"@genesislcap/foundation-comms": "15.14.2-FUI-2603.2",
|
|
147
|
+
"@genesislcap/foundation-events": "15.14.2-FUI-2603.2",
|
|
148
|
+
"@genesislcap/foundation-i18n": "15.14.2-FUI-2603.2",
|
|
149
|
+
"@genesislcap/foundation-logger": "15.14.2-FUI-2603.2",
|
|
150
|
+
"@genesislcap/foundation-state-machine": "15.14.2-FUI-2603.2",
|
|
151
|
+
"@genesislcap/foundation-store": "15.14.2-FUI-2603.2",
|
|
152
|
+
"@genesislcap/foundation-ui": "15.14.2-FUI-2603.2",
|
|
153
|
+
"@genesislcap/foundation-user": "15.14.2-FUI-2603.2",
|
|
154
|
+
"@genesislcap/foundation-utils": "15.14.2-FUI-2603.2",
|
|
155
|
+
"@genesislcap/foundation-zero": "15.14.2-FUI-2603.2",
|
|
156
156
|
"@microsoft/fast-components": "2.30.6",
|
|
157
157
|
"@microsoft/fast-element": "1.14.0",
|
|
158
158
|
"@microsoft/fast-foundation": "2.50.0",
|
|
@@ -173,5 +173,5 @@
|
|
|
173
173
|
"access": "public"
|
|
174
174
|
},
|
|
175
175
|
"customElements": "dist/custom-elements.json",
|
|
176
|
-
"gitHead": "
|
|
176
|
+
"gitHead": "470d546163b507cfe4bc8335cdb5955c40aeb58a"
|
|
177
177
|
}
|