@genesislcap/foundation-auth 15.14.2-FUI-2603.2 → 15.14.2-FUI-2603.3

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.
Files changed (52) hide show
  1. package/dist/dts/machine/actions.d.ts +0 -13
  2. package/dist/dts/machine/actions.d.ts.map +1 -1
  3. package/dist/dts/machine/errors.d.ts +0 -5
  4. package/dist/dts/machine/errors.d.ts.map +1 -1
  5. package/dist/dts/machine/guards.d.ts +1 -2
  6. package/dist/dts/machine/guards.d.ts.map +1 -1
  7. package/dist/dts/machine/machine.d.ts +0 -4
  8. package/dist/dts/machine/machine.d.ts.map +1 -1
  9. package/dist/dts/machine/types.d.ts +0 -2
  10. package/dist/dts/machine/types.d.ts.map +1 -1
  11. package/dist/dts/mapper/utils.d.ts +0 -11
  12. package/dist/dts/mapper/utils.d.ts.map +1 -1
  13. package/dist/dts/routes/base.d.ts +0 -6
  14. package/dist/dts/routes/base.d.ts.map +1 -1
  15. package/dist/dts/routes/forgot-password/forgot-password.d.ts.map +1 -1
  16. package/dist/dts/routes/reset-password/reset-password.d.ts +0 -2
  17. package/dist/dts/routes/reset-password/reset-password.d.ts.map +1 -1
  18. package/dist/dts/routes/reset-password/reset-password.template.d.ts.map +1 -1
  19. package/dist/esm/machine/actions.js +14 -16
  20. package/dist/esm/machine/errors.js +8 -39
  21. package/dist/esm/machine/guards.js +5 -13
  22. package/dist/esm/machine/machine.js +45 -90
  23. package/dist/esm/mapper/utils.js +0 -16
  24. package/dist/esm/routes/base.js +0 -20
  25. package/dist/esm/routes/forgot-password/forgot-password.js +0 -5
  26. package/dist/esm/routes/reset-password/reset-password.js +13 -19
  27. package/dist/esm/routes/reset-password/reset-password.template.js +2 -6
  28. package/dist/esm/translation.json +1 -2
  29. package/dist/foundation-auth.api.json +2 -175
  30. package/dist/foundation-auth.d.ts +1 -35
  31. package/package.json +19 -19
  32. package/dist/dts/machine/actions.test.d.ts +0 -2
  33. package/dist/dts/machine/actions.test.d.ts.map +0 -1
  34. package/dist/dts/machine/dto.d.ts +0 -34
  35. package/dist/dts/machine/dto.d.ts.map +0 -1
  36. package/dist/dts/machine/dto.test.d.ts +0 -2
  37. package/dist/dts/machine/dto.test.d.ts.map +0 -1
  38. package/dist/dts/machine/guards.test.d.ts +0 -2
  39. package/dist/dts/machine/guards.test.d.ts.map +0 -1
  40. package/dist/dts/mapper/utils.test.d.ts +0 -2
  41. package/dist/dts/mapper/utils.test.d.ts.map +0 -1
  42. package/dist/dts/routes/reset-password/reset-identity.d.ts +0 -38
  43. package/dist/dts/routes/reset-password/reset-identity.d.ts.map +0 -1
  44. package/dist/dts/routes/reset-password/reset-identity.test.d.ts +0 -2
  45. package/dist/dts/routes/reset-password/reset-identity.test.d.ts.map +0 -1
  46. package/dist/esm/machine/actions.test.js +0 -21
  47. package/dist/esm/machine/dto.js +0 -76
  48. package/dist/esm/machine/dto.test.js +0 -88
  49. package/dist/esm/machine/guards.test.js +0 -53
  50. package/dist/esm/mapper/utils.test.js +0 -56
  51. package/dist/esm/routes/reset-password/reset-identity.js +0 -99
  52. package/dist/esm/routes/reset-password/reset-identity.test.js +0 -55
@@ -1,76 +0,0 @@
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
- }
@@ -1,88 +0,0 @@
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();
@@ -1,53 +0,0 @@
1
- import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
2
- import { is200Nack } from './guards';
3
- import { isResetPasswordParams } from './types';
4
- const Suite = createLogicSuite('auth machine password reset guards');
5
- const nackDto = {
6
- MESSAGE_TYPE: 'EVENT_NACK',
7
- SOURCE_REF: 'ref',
8
- ERROR: [
9
- {
10
- '@type': 'StandardError',
11
- CODE: 'INTERNAL_ERROR',
12
- TEXT: 'Password validation failed',
13
- STATUS_CODE: '500 Internal Server Error',
14
- },
15
- ],
16
- WARNING: [],
17
- };
18
- Suite('is200Nack is true for FetchMachineOutput wrapping EVENT_NACK', () => {
19
- assert.ok(is200Nack({
20
- type: 'done.invoke.resetPasswordFetcher',
21
- output: { data: nackDto, response: { ok: true } },
22
- }));
23
- });
24
- Suite('is200Nack is true for EVENT_NACK even when HTTP is not ok', () => {
25
- assert.ok(is200Nack({
26
- type: 'done.invoke.resetPasswordFetcher',
27
- output: { data: nackDto, response: { ok: false } },
28
- }));
29
- });
30
- Suite('is200Nack is false for EVENT_ACK without errors', () => {
31
- assert.not.ok(is200Nack({
32
- type: 'done.invoke.resetPasswordFetcher',
33
- output: { data: { MESSAGE_TYPE: 'EVENT_ACK', SOURCE_REF: 'ref' }, response: { ok: true } },
34
- }));
35
- });
36
- Suite('isResetPasswordParams requires username, token and new password', () => {
37
- assert.ok(isResetPasswordParams({
38
- username: 'JaneDee',
39
- resetToken: 'token',
40
- newPassword: 'Password11*',
41
- }));
42
- assert.not.ok(isResetPasswordParams({
43
- username: '',
44
- resetToken: 'token',
45
- newPassword: 'Password11*',
46
- }));
47
- assert.not.ok(isResetPasswordParams({
48
- username: 'JaneDee',
49
- resetToken: '',
50
- newPassword: 'Password11*',
51
- }));
52
- });
53
- Suite.run();
@@ -1,56 +0,0 @@
1
- import { APIError } from '@genesislcap/foundation-state-machine';
2
- import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
3
- import { StatusCodes } from 'http-status-codes';
4
- import { nackErrorFromUnknown } from '../machine/errors';
5
- import { hasDTOErrors, isFailedAuthDTO, isNackDTO } from './utils';
6
- const Suite = createLogicSuite('password-reset response handling');
7
- const passwordValidationNack = {
8
- WARNING: [],
9
- ERROR: [
10
- {
11
- '@type': 'StandardError',
12
- CODE: 'INTERNAL_ERROR',
13
- TEXT: 'Password validation failed Password must be 8 or more characters in length.\nPassword must contain 1 or more special characters.',
14
- STATUS_CODE: '500 Internal Server Error',
15
- },
16
- ],
17
- MESSAGE_TYPE: 'EVENT_NACK',
18
- SOURCE_REF: '17605761-55cb-4b5a-a1ec-7235d3cae393',
19
- };
20
- const eventAck = {
21
- MESSAGE_TYPE: 'EVENT_ACK',
22
- SOURCE_REF: 'ack-ref',
23
- };
24
- Suite('detects EVENT_NACK as failure', () => {
25
- assert.ok(isNackDTO(passwordValidationNack));
26
- assert.ok(isFailedAuthDTO(passwordValidationNack));
27
- assert.ok(hasDTOErrors(passwordValidationNack));
28
- });
29
- Suite('detects ERROR payload as failure even without NACK suffix', () => {
30
- const ackWithErrors = Object.assign(Object.assign({}, eventAck), { ERROR: passwordValidationNack.ERROR });
31
- assert.not.ok(isNackDTO(ackWithErrors));
32
- assert.ok(hasDTOErrors(ackWithErrors));
33
- assert.ok(isFailedAuthDTO(ackWithErrors));
34
- });
35
- Suite('treats EVENT_ACK without ERROR as success', () => {
36
- assert.not.ok(isNackDTO(eventAck));
37
- assert.not.ok(hasDTOErrors(eventAck));
38
- assert.not.ok(isFailedAuthDTO(eventAck));
39
- });
40
- Suite('extracts strong-password error text from EVENT_NACK', () => {
41
- const error = nackErrorFromUnknown(passwordValidationNack);
42
- assert.ok(error);
43
- assert.ok(error.message.includes('Password validation failed'));
44
- assert.is(error.status, StatusCodes.INTERNAL_SERVER_ERROR);
45
- });
46
- Suite('extracts NACK from FetchMachineOutput and APIError wrappers', () => {
47
- const fromFetch = nackErrorFromUnknown({ data: passwordValidationNack, response: { ok: true } });
48
- assert.ok(fromFetch === null || fromFetch === void 0 ? void 0 : fromFetch.message.includes('Password validation failed'));
49
- const fromApiError = nackErrorFromUnknown(new APIError(StatusCodes.INTERNAL_SERVER_ERROR, passwordValidationNack));
50
- assert.ok(fromApiError === null || fromApiError === void 0 ? void 0 : fromApiError.message.includes('Password validation failed'));
51
- });
52
- Suite('does not treat EVENT_ACK as an error', () => {
53
- assert.is(nackErrorFromUnknown(eventAck), null);
54
- assert.is(nackErrorFromUnknown({ data: eventAck }), null);
55
- });
56
- Suite.run();
@@ -1,99 +0,0 @@
1
- /**
2
- * Identity captured during forgot-password so the token reset screen can
3
- * submit USER_NAME without asking the user to type it again.
4
- */
5
- export const RESET_IDENTITY_STORAGE_KEY = 'foundation-auth:password-reset-identity';
6
- const getStorage = (storage) => {
7
- if (storage) {
8
- return storage;
9
- }
10
- try {
11
- return globalThis.localStorage;
12
- }
13
- catch (_a) {
14
- return undefined;
15
- }
16
- };
17
- /**
18
- * @public
19
- */
20
- export function persistResetIdentity(identity, storage) {
21
- var _a, _b;
22
- if (!((_a = identity === null || identity === void 0 ? void 0 : identity.username) === null || _a === void 0 ? void 0 : _a.trim())) {
23
- return;
24
- }
25
- const target = getStorage(storage);
26
- if (!target) {
27
- return;
28
- }
29
- try {
30
- target.setItem(RESET_IDENTITY_STORAGE_KEY, JSON.stringify({
31
- username: identity.username.trim(),
32
- organisation: ((_b = identity.organisation) === null || _b === void 0 ? void 0 : _b.trim()) || undefined,
33
- }));
34
- }
35
- catch (_c) {
36
- // Storage may be unavailable (private mode, SSR).
37
- }
38
- }
39
- /**
40
- * @public
41
- */
42
- export function readResetIdentity(storage) {
43
- var _a, _b;
44
- const target = getStorage(storage);
45
- if (!target) {
46
- return undefined;
47
- }
48
- try {
49
- const raw = target.getItem(RESET_IDENTITY_STORAGE_KEY);
50
- if (!raw) {
51
- return undefined;
52
- }
53
- const parsed = JSON.parse(raw);
54
- if ((_a = parsed === null || parsed === void 0 ? void 0 : parsed.username) === null || _a === void 0 ? void 0 : _a.trim()) {
55
- return {
56
- username: parsed.username.trim(),
57
- organisation: ((_b = parsed.organisation) === null || _b === void 0 ? void 0 : _b.trim()) || undefined,
58
- };
59
- }
60
- }
61
- catch (_c) {
62
- // Ignore malformed payloads.
63
- }
64
- return undefined;
65
- }
66
- /**
67
- * @public
68
- */
69
- export function clearResetIdentity(storage) {
70
- const target = getStorage(storage);
71
- if (!target) {
72
- return;
73
- }
74
- try {
75
- target.removeItem(RESET_IDENTITY_STORAGE_KEY);
76
- }
77
- catch (_a) {
78
- // Storage may be unavailable.
79
- }
80
- }
81
- /**
82
- * @public
83
- */
84
- export function resolveResetUsername(queryParams = {}, stored) {
85
- return (queryParams.username ||
86
- queryParams.user ||
87
- queryParams.USER_NAME ||
88
- (stored === null || stored === void 0 ? void 0 : stored.username) ||
89
- '').trim();
90
- }
91
- /**
92
- * Username is required by EVENT_PASSWORD_RESET_ACTION. Hide it only when a
93
- * reset token is present and the username is already known.
94
- *
95
- * @public
96
- */
97
- export function shouldShowIdentityFields(passwordResetToken, username) {
98
- return !passwordResetToken || !username.trim();
99
- }
@@ -1,55 +0,0 @@
1
- import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
2
- import { clearResetIdentity, persistResetIdentity, readResetIdentity, resolveResetUsername, RESET_IDENTITY_STORAGE_KEY, shouldShowIdentityFields, } from './reset-identity';
3
- const Suite = createLogicSuite('reset-identity');
4
- const createMemoryStorage = () => {
5
- const map = new Map();
6
- return {
7
- get length() {
8
- return map.size;
9
- },
10
- clear: () => map.clear(),
11
- getItem: (key) => { var _a; return (_a = map.get(key)) !== null && _a !== void 0 ? _a : null; },
12
- key: (index) => { var _a; return (_a = [...map.keys()][index]) !== null && _a !== void 0 ? _a : null; },
13
- removeItem: (key) => {
14
- map.delete(key);
15
- },
16
- setItem: (key, value) => {
17
- map.set(key, value);
18
- },
19
- };
20
- };
21
- Suite('hides username when token is present and username is known', () => {
22
- assert.not.ok(shouldShowIdentityFields('reset-token', 'JaneDee'));
23
- });
24
- Suite('shows username when token is present but username is missing', () => {
25
- assert.ok(shouldShowIdentityFields('reset-token', ''));
26
- assert.ok(shouldShowIdentityFields('reset-token', ' '));
27
- });
28
- Suite('shows username when token is absent', () => {
29
- assert.ok(shouldShowIdentityFields('', 'JaneDee'));
30
- assert.ok(shouldShowIdentityFields(undefined, ''));
31
- });
32
- Suite('resolves username from query params before stored identity', () => {
33
- const stored = { username: 'stored-user' };
34
- assert.is(resolveResetUsername({ username: 'query-user' }, stored), 'query-user');
35
- assert.is(resolveResetUsername({ user: 'alias-user' }, stored), 'alias-user');
36
- assert.is(resolveResetUsername({ USER_NAME: 'legacy-user' }, stored), 'legacy-user');
37
- });
38
- Suite('resolves username from stored identity when query has no username', () => {
39
- assert.is(resolveResetUsername({ password: 'token' }, { username: 'stored-user' }), 'stored-user');
40
- assert.is(resolveResetUsername({ password: 'token' }), '');
41
- });
42
- Suite('persists, reads and clears reset identity', () => {
43
- const storage = createMemoryStorage();
44
- persistResetIdentity({ username: ' JaneDee ', organisation: ' FBN ' }, storage);
45
- assert.equal(readResetIdentity(storage), { username: 'JaneDee', organisation: 'FBN' });
46
- assert.ok(storage.getItem(RESET_IDENTITY_STORAGE_KEY));
47
- clearResetIdentity(storage);
48
- assert.is(readResetIdentity(storage), undefined);
49
- });
50
- Suite('does not persist empty username', () => {
51
- const storage = createMemoryStorage();
52
- persistResetIdentity({ username: ' ' }, storage);
53
- assert.is(readResetIdentity(storage), undefined);
54
- });
55
- Suite.run();