@alanszp/express 12.0.2 → 13.0.0
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/helpers/getRequestBaseLog.d.ts +2 -0
- package/dist/helpers/getRequestBaseLog.js +9 -0
- package/dist/helpers/getRequestBaseLog.js.map +1 -0
- package/dist/helpers/renderErrorJson.d.ts +1 -0
- package/dist/helpers/renderErrorJson.js +10 -1
- package/dist/helpers/renderErrorJson.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/middlewares/errorRequestHandlerMiddleware.d.ts +4 -0
- package/dist/middlewares/errorRequestHandlerMiddleware.js +68 -0
- package/dist/middlewares/errorRequestHandlerMiddleware.js.map +1 -0
- package/dist/middlewares/hasPermissions.d.ts +20 -0
- package/dist/middlewares/hasPermissions.js +90 -0
- package/dist/middlewares/hasPermissions.js.map +1 -0
- package/dist/middlewares/hasRoles.js +4 -4
- package/dist/middlewares/hasRoles.js.map +1 -1
- package/package.json +9 -10
- package/.gitignore +0 -3
- package/.npmignore +0 -3
- package/dist/middlewares/returnInternalServerError.d.ts +0 -4
- package/dist/middlewares/returnInternalServerError.js +0 -10
- package/dist/middlewares/returnInternalServerError.js.map +0 -1
- package/node_modules/@jest/expect-utils/LICENSE +0 -21
- package/node_modules/@jest/expect-utils/README.md +0 -5
- package/node_modules/@jest/expect-utils/build/index.d.ts +0 -93
- package/node_modules/@jest/expect-utils/build/index.js +0 -37
- package/node_modules/@jest/expect-utils/build/jasmineUtils.js +0 -282
- package/node_modules/@jest/expect-utils/build/types.js +0 -1
- package/node_modules/@jest/expect-utils/build/utils.js +0 -457
- package/node_modules/@jest/expect-utils/package.json +0 -33
- package/node_modules/@jest/types/LICENSE +0 -21
- package/node_modules/@jest/types/README.md +0 -30
- package/node_modules/@jest/types/build/Circus.js +0 -1
- package/node_modules/@jest/types/build/Config.js +0 -1
- package/node_modules/@jest/types/build/Global.js +0 -1
- package/node_modules/@jest/types/build/TestResult.js +0 -1
- package/node_modules/@jest/types/build/Transform.js +0 -1
- package/node_modules/@jest/types/build/index.d.ts +0 -1166
- package/node_modules/@jest/types/build/index.js +0 -1
- package/node_modules/@jest/types/package.json +0 -38
- package/node_modules/@types/jest/LICENSE +0 -21
- package/node_modules/@types/jest/README.md +0 -17
- package/node_modules/@types/jest/index.d.ts +0 -1608
- package/node_modules/@types/jest/package.json +0 -159
- package/node_modules/@types/node/LICENSE +0 -21
- package/node_modules/@types/node/README.md +0 -15
- package/node_modules/@types/node/assert/strict.d.ts +0 -8
- package/node_modules/@types/node/assert.d.ts +0 -996
- package/node_modules/@types/node/async_hooks.d.ts +0 -539
- package/node_modules/@types/node/buffer.d.ts +0 -2362
- package/node_modules/@types/node/child_process.d.ts +0 -1540
- package/node_modules/@types/node/cluster.d.ts +0 -432
- package/node_modules/@types/node/console.d.ts +0 -415
- package/node_modules/@types/node/constants.d.ts +0 -19
- package/node_modules/@types/node/crypto.d.ts +0 -4487
- package/node_modules/@types/node/dgram.d.ts +0 -596
- package/node_modules/@types/node/diagnostics_channel.d.ts +0 -545
- package/node_modules/@types/node/dns/promises.d.ts +0 -425
- package/node_modules/@types/node/dns.d.ts +0 -809
- package/node_modules/@types/node/dom-events.d.ts +0 -122
- package/node_modules/@types/node/domain.d.ts +0 -170
- package/node_modules/@types/node/events.d.ts +0 -879
- package/node_modules/@types/node/fs/promises.d.ts +0 -1239
- package/node_modules/@types/node/fs.d.ts +0 -4311
- package/node_modules/@types/node/globals.d.ts +0 -411
- package/node_modules/@types/node/globals.global.d.ts +0 -1
- package/node_modules/@types/node/http.d.ts +0 -1887
- package/node_modules/@types/node/http2.d.ts +0 -2382
- package/node_modules/@types/node/https.d.ts +0 -550
- package/node_modules/@types/node/index.d.ts +0 -88
- package/node_modules/@types/node/inspector.d.ts +0 -2747
- package/node_modules/@types/node/module.d.ts +0 -315
- package/node_modules/@types/node/net.d.ts +0 -949
- package/node_modules/@types/node/os.d.ts +0 -478
- package/node_modules/@types/node/package.json +0 -229
- package/node_modules/@types/node/path.d.ts +0 -191
- package/node_modules/@types/node/perf_hooks.d.ts +0 -645
- package/node_modules/@types/node/process.d.ts +0 -1561
- package/node_modules/@types/node/punycode.d.ts +0 -117
- package/node_modules/@types/node/querystring.d.ts +0 -141
- package/node_modules/@types/node/readline/promises.d.ts +0 -150
- package/node_modules/@types/node/readline.d.ts +0 -539
- package/node_modules/@types/node/repl.d.ts +0 -430
- package/node_modules/@types/node/stream/consumers.d.ts +0 -12
- package/node_modules/@types/node/stream/promises.d.ts +0 -83
- package/node_modules/@types/node/stream/web.d.ts +0 -366
- package/node_modules/@types/node/stream.d.ts +0 -1701
- package/node_modules/@types/node/string_decoder.d.ts +0 -67
- package/node_modules/@types/node/test.d.ts +0 -1465
- package/node_modules/@types/node/timers/promises.d.ts +0 -93
- package/node_modules/@types/node/timers.d.ts +0 -240
- package/node_modules/@types/node/tls.d.ts +0 -1210
- package/node_modules/@types/node/trace_events.d.ts +0 -182
- package/node_modules/@types/node/ts4.8/assert/strict.d.ts +0 -8
- package/node_modules/@types/node/ts4.8/assert.d.ts +0 -996
- package/node_modules/@types/node/ts4.8/async_hooks.d.ts +0 -539
- package/node_modules/@types/node/ts4.8/buffer.d.ts +0 -2362
- package/node_modules/@types/node/ts4.8/child_process.d.ts +0 -1540
- package/node_modules/@types/node/ts4.8/cluster.d.ts +0 -432
- package/node_modules/@types/node/ts4.8/console.d.ts +0 -415
- package/node_modules/@types/node/ts4.8/constants.d.ts +0 -19
- package/node_modules/@types/node/ts4.8/crypto.d.ts +0 -4487
- package/node_modules/@types/node/ts4.8/dgram.d.ts +0 -596
- package/node_modules/@types/node/ts4.8/diagnostics_channel.d.ts +0 -545
- package/node_modules/@types/node/ts4.8/dns/promises.d.ts +0 -425
- package/node_modules/@types/node/ts4.8/dns.d.ts +0 -809
- package/node_modules/@types/node/ts4.8/dom-events.d.ts +0 -122
- package/node_modules/@types/node/ts4.8/domain.d.ts +0 -170
- package/node_modules/@types/node/ts4.8/events.d.ts +0 -879
- package/node_modules/@types/node/ts4.8/fs/promises.d.ts +0 -1239
- package/node_modules/@types/node/ts4.8/fs.d.ts +0 -4311
- package/node_modules/@types/node/ts4.8/globals.d.ts +0 -411
- package/node_modules/@types/node/ts4.8/globals.global.d.ts +0 -1
- package/node_modules/@types/node/ts4.8/http.d.ts +0 -1887
- package/node_modules/@types/node/ts4.8/http2.d.ts +0 -2382
- package/node_modules/@types/node/ts4.8/https.d.ts +0 -550
- package/node_modules/@types/node/ts4.8/index.d.ts +0 -88
- package/node_modules/@types/node/ts4.8/inspector.d.ts +0 -2747
- package/node_modules/@types/node/ts4.8/module.d.ts +0 -315
- package/node_modules/@types/node/ts4.8/net.d.ts +0 -949
- package/node_modules/@types/node/ts4.8/os.d.ts +0 -478
- package/node_modules/@types/node/ts4.8/path.d.ts +0 -191
- package/node_modules/@types/node/ts4.8/perf_hooks.d.ts +0 -645
- package/node_modules/@types/node/ts4.8/process.d.ts +0 -1561
- package/node_modules/@types/node/ts4.8/punycode.d.ts +0 -117
- package/node_modules/@types/node/ts4.8/querystring.d.ts +0 -141
- package/node_modules/@types/node/ts4.8/readline/promises.d.ts +0 -150
- package/node_modules/@types/node/ts4.8/readline.d.ts +0 -539
- package/node_modules/@types/node/ts4.8/repl.d.ts +0 -430
- package/node_modules/@types/node/ts4.8/stream/consumers.d.ts +0 -12
- package/node_modules/@types/node/ts4.8/stream/promises.d.ts +0 -83
- package/node_modules/@types/node/ts4.8/stream/web.d.ts +0 -366
- package/node_modules/@types/node/ts4.8/stream.d.ts +0 -1701
- package/node_modules/@types/node/ts4.8/string_decoder.d.ts +0 -67
- package/node_modules/@types/node/ts4.8/test.d.ts +0 -1465
- package/node_modules/@types/node/ts4.8/timers/promises.d.ts +0 -93
- package/node_modules/@types/node/ts4.8/timers.d.ts +0 -240
- package/node_modules/@types/node/ts4.8/tls.d.ts +0 -1210
- package/node_modules/@types/node/ts4.8/trace_events.d.ts +0 -182
- package/node_modules/@types/node/ts4.8/tty.d.ts +0 -208
- package/node_modules/@types/node/ts4.8/url.d.ts +0 -927
- package/node_modules/@types/node/ts4.8/util.d.ts +0 -2183
- package/node_modules/@types/node/ts4.8/v8.d.ts +0 -764
- package/node_modules/@types/node/ts4.8/vm.d.ts +0 -903
- package/node_modules/@types/node/ts4.8/wasi.d.ts +0 -179
- package/node_modules/@types/node/ts4.8/worker_threads.d.ts +0 -691
- package/node_modules/@types/node/ts4.8/zlib.d.ts +0 -517
- package/node_modules/@types/node/tty.d.ts +0 -208
- package/node_modules/@types/node/url.d.ts +0 -927
- package/node_modules/@types/node/util.d.ts +0 -2183
- package/node_modules/@types/node/v8.d.ts +0 -764
- package/node_modules/@types/node/vm.d.ts +0 -903
- package/node_modules/@types/node/wasi.d.ts +0 -179
- package/node_modules/@types/node/worker_threads.d.ts +0 -691
- package/node_modules/@types/node/zlib.d.ts +0 -517
- package/src/helpers/getIp.ts +0 -5
- package/src/helpers/getRequestLogger.ts +0 -6
- package/src/helpers/now.ts +0 -3
- package/src/helpers/renderErrorJson.ts +0 -29
- package/src/index.ts +0 -10
- package/src/middlewares/accessLogger.ts +0 -34
- package/src/middlewares/auditLog.ts +0 -52
- package/src/middlewares/authedForOrg.ts +0 -50
- package/src/middlewares/authenticateUser.test.ts +0 -403
- package/src/middlewares/authenticateUser.ts +0 -171
- package/src/middlewares/createContext.test.ts +0 -85
- package/src/middlewares/createContext.ts +0 -49
- package/src/middlewares/hasRoles.ts +0 -24
- package/src/middlewares/jsonBodyParser.ts +0 -22
- package/src/middlewares/returnInternalServerError.ts +0 -15
- package/src/middlewares/returnNotFound.ts +0 -11
- package/src/test/mocks/authOptionsMocks.ts +0 -35
- package/src/test/mocks/expressMocks.ts +0 -33
- package/src/test/mocks/jwtUserMocks.ts +0 -19
- package/src/test/setup.test.ts +0 -15
- package/src/test/setup.ts +0 -3
- package/src/types/AuthMethod.ts +0 -27
- package/src/types/GenericRequest.ts +0 -3
- package/src/types/custom.d.ts +0 -20
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { NextFunction, Response } from "express";
|
|
2
|
-
import { getRequestLogger } from "../helpers/getRequestLogger";
|
|
3
|
-
import { GenericRequest } from "../types/GenericRequest";
|
|
4
|
-
import { render401Error, render404Error } from "../helpers/renderErrorJson";
|
|
5
|
-
|
|
6
|
-
function response401(res: Response): void {
|
|
7
|
-
res.status(401).json(render401Error(["jwt"]));
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function authForOrg(
|
|
11
|
-
req: GenericRequest,
|
|
12
|
-
res: Response,
|
|
13
|
-
next: NextFunction
|
|
14
|
-
): void {
|
|
15
|
-
const { orgReference } = req.params;
|
|
16
|
-
const logger = getRequestLogger(req).child({ org: orgReference });
|
|
17
|
-
try {
|
|
18
|
-
if (!orgReference) {
|
|
19
|
-
logger.error("middleware.authForOrg.error.noOrgReferenceInPath");
|
|
20
|
-
return response401(res);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (!req.context?.jwtUser?.organizationReference) {
|
|
24
|
-
logger.info("middleware.authForOrg.error.noOrgInJwt", {
|
|
25
|
-
jwtUser: req.context?.jwtUser || null,
|
|
26
|
-
});
|
|
27
|
-
return response401(res);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const jwtOrg = req.context.jwtUser.organizationReference;
|
|
31
|
-
if (jwtOrg !== orgReference) {
|
|
32
|
-
if (jwtOrg === "lara") {
|
|
33
|
-
req.context.jwtUser.organizationReference = orgReference;
|
|
34
|
-
return next();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
logger.info("middleware.authForOrg.error.nonMatch", {
|
|
38
|
-
jwtOrg,
|
|
39
|
-
});
|
|
40
|
-
return response401(res);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return next();
|
|
44
|
-
} catch (error: unknown) {
|
|
45
|
-
logger.info("middleware.authForOrg.error.noOrganization", {
|
|
46
|
-
error,
|
|
47
|
-
});
|
|
48
|
-
res.status(404).json(render404Error());
|
|
49
|
-
}
|
|
50
|
-
}
|
|
@@ -1,403 +0,0 @@
|
|
|
1
|
-
import { AuthMethods, createAuthContext } from "./authenticateUser";
|
|
2
|
-
import { verifyJWT } from "@alanszp/jwt";
|
|
3
|
-
import {
|
|
4
|
-
mockNext,
|
|
5
|
-
mockRequest,
|
|
6
|
-
mockResponse,
|
|
7
|
-
} from "../test/mocks/expressMocks";
|
|
8
|
-
import { laraJwtUserMock, userJwtUserMock } from "../test/mocks/jwtUserMocks";
|
|
9
|
-
import {
|
|
10
|
-
apiKeyAuthOptions,
|
|
11
|
-
bothMethodsAuthOptions,
|
|
12
|
-
jwtAuthOptions,
|
|
13
|
-
verifyOptions,
|
|
14
|
-
} from "../test/mocks/authOptionsMocks";
|
|
15
|
-
|
|
16
|
-
jest.mock("@alanszp/jwt");
|
|
17
|
-
|
|
18
|
-
describe("AuthenticateUser", () => {
|
|
19
|
-
describe("authentication with only JWT", () => {
|
|
20
|
-
describe("when jwt verifies correctly", () => {
|
|
21
|
-
it("should authenticate correctly and call next", async () => {
|
|
22
|
-
(verifyJWT as jest.Mock).mockResolvedValueOnce(userJwtUserMock);
|
|
23
|
-
|
|
24
|
-
const req = mockRequest("Bearer token");
|
|
25
|
-
const res = mockResponse();
|
|
26
|
-
const next = mockNext();
|
|
27
|
-
|
|
28
|
-
await createAuthContext(jwtAuthOptions)([AuthMethods.JWT])(
|
|
29
|
-
req,
|
|
30
|
-
res,
|
|
31
|
-
next
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
expect(verifyJWT).toBeCalledWith("publicKey", "token", verifyOptions);
|
|
35
|
-
expect(res.status).toHaveBeenCalledTimes(0);
|
|
36
|
-
expect(res.json).toHaveBeenCalledTimes(0);
|
|
37
|
-
expect(req.context.jwtUser).toMatchObject(userJwtUserMock);
|
|
38
|
-
expect(req.context.authenticated).toStrictEqual(["jwt"]);
|
|
39
|
-
expect(next).toBeCalledWith();
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe("when jwt verifies incorrectly", () => {
|
|
44
|
-
it("should not authenticate, should not call next, and it should return 401", async () => {
|
|
45
|
-
(verifyJWT as jest.Mock).mockResolvedValueOnce(undefined);
|
|
46
|
-
|
|
47
|
-
const req = mockRequest("Bearer token");
|
|
48
|
-
const res = mockResponse();
|
|
49
|
-
const next = mockNext();
|
|
50
|
-
|
|
51
|
-
await createAuthContext(jwtAuthOptions)([AuthMethods.JWT])(
|
|
52
|
-
req,
|
|
53
|
-
res,
|
|
54
|
-
next
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
expect(verifyJWT).toBeCalledWith("publicKey", "token", verifyOptions);
|
|
58
|
-
expect(res.status).toHaveBeenCalledWith(401);
|
|
59
|
-
expect(res.json).toHaveBeenCalledTimes(1);
|
|
60
|
-
expect(req.context.jwtUser).toBe(undefined);
|
|
61
|
-
expect(req.context.authenticated).toEqual([]);
|
|
62
|
-
expect(next).toHaveBeenCalledTimes(0);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe("when jwt doesn't exist", () => {
|
|
67
|
-
it("should not verify JWT, should not authenticate, should not call next, and it should return 401", async () => {
|
|
68
|
-
const req = mockRequest("aa");
|
|
69
|
-
const res = mockResponse();
|
|
70
|
-
const next = mockNext();
|
|
71
|
-
|
|
72
|
-
await createAuthContext(jwtAuthOptions)([AuthMethods.JWT])(
|
|
73
|
-
req,
|
|
74
|
-
res,
|
|
75
|
-
next
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
expect(verifyJWT).toHaveBeenCalledTimes(0);
|
|
79
|
-
expect(res.status).toHaveBeenCalledWith(401);
|
|
80
|
-
expect(res.json).toHaveBeenCalledTimes(1);
|
|
81
|
-
expect(req.context.jwtUser).toBe(undefined);
|
|
82
|
-
expect(req.context.authenticated).toEqual([]);
|
|
83
|
-
expect(next).toHaveBeenCalledTimes(0);
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe("authentication with only API KEY", () => {
|
|
89
|
-
describe("when api key verifies correctly", () => {
|
|
90
|
-
it("should authenticate correctly and call next", async () => {
|
|
91
|
-
const req = mockRequest("token");
|
|
92
|
-
const res = mockResponse();
|
|
93
|
-
const next = mockNext();
|
|
94
|
-
|
|
95
|
-
await createAuthContext(apiKeyAuthOptions)([AuthMethods.API_KEY])(
|
|
96
|
-
req,
|
|
97
|
-
res,
|
|
98
|
-
next
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
expect(verifyJWT).toHaveBeenCalledTimes(0);
|
|
102
|
-
expect(res.status).toHaveBeenCalledTimes(0);
|
|
103
|
-
expect(res.json).toHaveBeenCalledTimes(0);
|
|
104
|
-
expect(req.context.jwtUser).toMatchObject(laraJwtUserMock);
|
|
105
|
-
expect(req.context.authenticated).toStrictEqual(["api_key"]);
|
|
106
|
-
expect(next).toBeCalledWith();
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
describe("when api key verifies incorrectly", () => {
|
|
111
|
-
it("should not authenticate, should not call next, and it should return 401", async () => {
|
|
112
|
-
const req = mockRequest("invalidToken");
|
|
113
|
-
const res = mockResponse();
|
|
114
|
-
const next = mockNext();
|
|
115
|
-
|
|
116
|
-
await createAuthContext(apiKeyAuthOptions)([AuthMethods.API_KEY])(
|
|
117
|
-
req,
|
|
118
|
-
res,
|
|
119
|
-
next
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
expect(verifyJWT).toHaveBeenCalledTimes(0);
|
|
123
|
-
expect(res.status).toHaveBeenCalledWith(401);
|
|
124
|
-
expect(res.json).toHaveBeenCalledTimes(1);
|
|
125
|
-
expect(req.context.jwtUser).toBe(undefined);
|
|
126
|
-
expect(req.context.authenticated).toEqual([]);
|
|
127
|
-
expect(next).toHaveBeenCalledTimes(0);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
describe("when api key doesn't exist", () => {
|
|
132
|
-
it("should not verify api key, should not authenticate, should not call next, and it should return 401", async () => {
|
|
133
|
-
const req = mockRequest(undefined as any);
|
|
134
|
-
const res = mockResponse();
|
|
135
|
-
const next = mockNext();
|
|
136
|
-
|
|
137
|
-
await createAuthContext(apiKeyAuthOptions)([AuthMethods.API_KEY])(
|
|
138
|
-
req,
|
|
139
|
-
res,
|
|
140
|
-
next
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
expect(verifyJWT).toHaveBeenCalledTimes(0);
|
|
144
|
-
expect(res.status).toHaveBeenCalledWith(401);
|
|
145
|
-
expect(res.json).toHaveBeenCalledTimes(1);
|
|
146
|
-
expect(req.context.jwtUser).toBe(undefined);
|
|
147
|
-
expect(req.context.authenticated).toEqual([]);
|
|
148
|
-
expect(next).toHaveBeenCalledTimes(0);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
describe("authentication with JWT and API KEY", () => {
|
|
154
|
-
describe("using both methods", () => {
|
|
155
|
-
describe("when api key verifies correctly", () => {
|
|
156
|
-
it("should authenticate correctly and call next", async () => {
|
|
157
|
-
const req = mockRequest("token");
|
|
158
|
-
const res = mockResponse();
|
|
159
|
-
const next = mockNext();
|
|
160
|
-
|
|
161
|
-
await createAuthContext(bothMethodsAuthOptions)([
|
|
162
|
-
AuthMethods.API_KEY,
|
|
163
|
-
AuthMethods.JWT,
|
|
164
|
-
])(req, res, next);
|
|
165
|
-
|
|
166
|
-
expect(verifyJWT).toHaveBeenCalledTimes(0);
|
|
167
|
-
expect(res.status).toHaveBeenCalledTimes(0);
|
|
168
|
-
expect(res.json).toHaveBeenCalledTimes(0);
|
|
169
|
-
expect(req.context.jwtUser).toMatchObject(laraJwtUserMock);
|
|
170
|
-
expect(req.context.authenticated).toStrictEqual(["api_key"]);
|
|
171
|
-
expect(next).toHaveBeenCalledWith();
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
describe("when api key verifies incorrectly", () => {
|
|
176
|
-
it("should not authenticate, should not call next, and it should return 401", async () => {
|
|
177
|
-
const req = mockRequest("invalidToken");
|
|
178
|
-
const res = mockResponse();
|
|
179
|
-
const next = mockNext();
|
|
180
|
-
|
|
181
|
-
await createAuthContext(bothMethodsAuthOptions)([
|
|
182
|
-
AuthMethods.API_KEY,
|
|
183
|
-
AuthMethods.JWT,
|
|
184
|
-
])(req, res, next);
|
|
185
|
-
|
|
186
|
-
expect(verifyJWT).toHaveBeenCalledTimes(0);
|
|
187
|
-
expect(res.status).toHaveBeenCalledWith(401);
|
|
188
|
-
expect(res.json).toHaveBeenCalledTimes(1);
|
|
189
|
-
expect(req.context.jwtUser).toBe(undefined);
|
|
190
|
-
expect(req.context.authenticated).toEqual([]);
|
|
191
|
-
expect(next).toHaveBeenCalledTimes(0);
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
describe("when jwt verifies correctly", () => {
|
|
196
|
-
it("should authenticate correctly and call next", async () => {
|
|
197
|
-
(verifyJWT as jest.Mock).mockResolvedValueOnce(userJwtUserMock);
|
|
198
|
-
|
|
199
|
-
const req = mockRequest("Bearer token");
|
|
200
|
-
const res = mockResponse();
|
|
201
|
-
const next = mockNext();
|
|
202
|
-
|
|
203
|
-
await createAuthContext(bothMethodsAuthOptions)([
|
|
204
|
-
AuthMethods.API_KEY,
|
|
205
|
-
AuthMethods.JWT,
|
|
206
|
-
])(req, res, next);
|
|
207
|
-
|
|
208
|
-
expect(verifyJWT).toBeCalledWith("publicKey", "token", {
|
|
209
|
-
issuer: "issuer",
|
|
210
|
-
audience: "audience",
|
|
211
|
-
});
|
|
212
|
-
expect(res.status).toHaveBeenCalledTimes(0);
|
|
213
|
-
expect(res.json).toHaveBeenCalledTimes(0);
|
|
214
|
-
|
|
215
|
-
expect(req.context.jwtUser).toMatchObject(userJwtUserMock);
|
|
216
|
-
|
|
217
|
-
expect(req.context.authenticated).toStrictEqual(["jwt"]);
|
|
218
|
-
|
|
219
|
-
expect(next).toHaveBeenCalledWith();
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
describe("when jwt verifies incorrectly", () => {
|
|
224
|
-
it("should not authenticate, should not call next, and it should return 401", async () => {
|
|
225
|
-
(verifyJWT as jest.Mock).mockResolvedValueOnce(undefined);
|
|
226
|
-
|
|
227
|
-
const req = mockRequest("Bearer token");
|
|
228
|
-
const res = mockResponse();
|
|
229
|
-
const next = mockNext();
|
|
230
|
-
|
|
231
|
-
await createAuthContext(bothMethodsAuthOptions)([
|
|
232
|
-
AuthMethods.API_KEY,
|
|
233
|
-
AuthMethods.JWT,
|
|
234
|
-
])(req, res, next);
|
|
235
|
-
|
|
236
|
-
expect(verifyJWT).toBeCalledWith("publicKey", "token", {
|
|
237
|
-
issuer: "issuer",
|
|
238
|
-
audience: "audience",
|
|
239
|
-
});
|
|
240
|
-
expect(res.status).toHaveBeenCalledWith(401);
|
|
241
|
-
expect(res.json).toHaveBeenCalledTimes(1);
|
|
242
|
-
expect(req.context.jwtUser).toBe(undefined);
|
|
243
|
-
expect(req.context.authenticated).toEqual([]);
|
|
244
|
-
expect(next).toHaveBeenCalledTimes(0);
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
describe("when jwt doesnt exist", () => {
|
|
249
|
-
it("should not authenticate, should not call next, and it should return 401", async () => {
|
|
250
|
-
const req = mockRequest(undefined as any);
|
|
251
|
-
const res = mockResponse();
|
|
252
|
-
const next = mockNext();
|
|
253
|
-
|
|
254
|
-
await createAuthContext(bothMethodsAuthOptions)([
|
|
255
|
-
AuthMethods.API_KEY,
|
|
256
|
-
AuthMethods.JWT,
|
|
257
|
-
])(req, res, next);
|
|
258
|
-
|
|
259
|
-
expect(verifyJWT).toHaveBeenCalledTimes(0);
|
|
260
|
-
expect(res.status).toHaveBeenCalledWith(401);
|
|
261
|
-
expect(res.json).toHaveBeenCalledTimes(1);
|
|
262
|
-
expect(req.context.jwtUser).toBe(undefined);
|
|
263
|
-
expect(req.context.authenticated).toEqual([]);
|
|
264
|
-
expect(next).toHaveBeenCalledTimes(0);
|
|
265
|
-
});
|
|
266
|
-
});
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
describe("using jwt method", () => {
|
|
270
|
-
describe("when jwt verifies correctly", () => {
|
|
271
|
-
it("should authenticate correctly and call next", async () => {
|
|
272
|
-
(verifyJWT as jest.Mock).mockResolvedValueOnce(userJwtUserMock);
|
|
273
|
-
|
|
274
|
-
const req = mockRequest("Bearer token");
|
|
275
|
-
const res = mockResponse();
|
|
276
|
-
const next = mockNext();
|
|
277
|
-
|
|
278
|
-
await createAuthContext(bothMethodsAuthOptions)([AuthMethods.JWT])(
|
|
279
|
-
req,
|
|
280
|
-
res,
|
|
281
|
-
next
|
|
282
|
-
);
|
|
283
|
-
|
|
284
|
-
expect(verifyJWT).toBeCalledWith("publicKey", "token", {
|
|
285
|
-
issuer: "issuer",
|
|
286
|
-
audience: "audience",
|
|
287
|
-
});
|
|
288
|
-
expect(res.status).toHaveBeenCalledTimes(0);
|
|
289
|
-
expect(res.json).toHaveBeenCalledTimes(0);
|
|
290
|
-
expect(req.context.jwtUser).toMatchObject(userJwtUserMock);
|
|
291
|
-
expect(req.context.authenticated).toStrictEqual(["jwt"]);
|
|
292
|
-
expect(next).toHaveBeenCalledWith();
|
|
293
|
-
});
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
describe("when jwt verifies incorrectly", () => {
|
|
297
|
-
it("should not authenticate, should not call next, and it should return 401", async () => {
|
|
298
|
-
(verifyJWT as jest.Mock).mockResolvedValueOnce(undefined);
|
|
299
|
-
|
|
300
|
-
const req = mockRequest("Bearer invalidToken");
|
|
301
|
-
const res = mockResponse();
|
|
302
|
-
const next = mockNext();
|
|
303
|
-
|
|
304
|
-
await createAuthContext(bothMethodsAuthOptions)([AuthMethods.JWT])(
|
|
305
|
-
req,
|
|
306
|
-
res,
|
|
307
|
-
next
|
|
308
|
-
);
|
|
309
|
-
|
|
310
|
-
expect(verifyJWT).toBeCalledWith("publicKey", "invalidToken", {
|
|
311
|
-
issuer: "issuer",
|
|
312
|
-
audience: "audience",
|
|
313
|
-
});
|
|
314
|
-
expect(res.status).toHaveBeenCalledWith(401);
|
|
315
|
-
expect(res.json).toHaveBeenCalledTimes(1);
|
|
316
|
-
expect(req.context.jwtUser).toBe(undefined);
|
|
317
|
-
expect(req.context.authenticated).toEqual([]);
|
|
318
|
-
expect(next).toHaveBeenCalledTimes(0);
|
|
319
|
-
});
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
describe("when api key verifies correctly", () => {
|
|
323
|
-
it("should authenticate correctly and call next", async () => {
|
|
324
|
-
const req = mockRequest("token");
|
|
325
|
-
const res = mockResponse();
|
|
326
|
-
const next = mockNext();
|
|
327
|
-
|
|
328
|
-
await createAuthContext(bothMethodsAuthOptions)([
|
|
329
|
-
AuthMethods.API_KEY,
|
|
330
|
-
])(req, res, next);
|
|
331
|
-
|
|
332
|
-
expect(verifyJWT).toHaveBeenCalledTimes(0);
|
|
333
|
-
expect(res.status).toHaveBeenCalledTimes(0);
|
|
334
|
-
expect(res.json).toHaveBeenCalledTimes(0);
|
|
335
|
-
|
|
336
|
-
expect(req.context.jwtUser).toMatchObject(laraJwtUserMock);
|
|
337
|
-
expect(req.context.authenticated).toStrictEqual(["api_key"]);
|
|
338
|
-
expect(next).toHaveBeenCalledWith();
|
|
339
|
-
});
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
describe("when api key verifies incorrectly", () => {
|
|
343
|
-
it("should not authenticate, should not call next, and it should return 401", async () => {
|
|
344
|
-
const req = mockRequest("invalidToken");
|
|
345
|
-
const res = mockResponse();
|
|
346
|
-
const next = mockNext();
|
|
347
|
-
|
|
348
|
-
await createAuthContext(bothMethodsAuthOptions)([
|
|
349
|
-
AuthMethods.API_KEY,
|
|
350
|
-
AuthMethods.JWT,
|
|
351
|
-
])(req, res, next);
|
|
352
|
-
|
|
353
|
-
expect(verifyJWT).toHaveBeenCalledTimes(0);
|
|
354
|
-
expect(res.status).toHaveBeenCalledWith(401);
|
|
355
|
-
expect(res.json).toHaveBeenCalledTimes(1);
|
|
356
|
-
expect(req.context.jwtUser).toBe(undefined);
|
|
357
|
-
expect(req.context.authenticated).toEqual([]);
|
|
358
|
-
expect(next).toHaveBeenCalledTimes(0);
|
|
359
|
-
});
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
describe("when jwt doesnt exist", () => {
|
|
363
|
-
it("should not authenticate, should not call next, and it should return 401", async () => {
|
|
364
|
-
const req = mockRequest("aaa");
|
|
365
|
-
const res = mockResponse();
|
|
366
|
-
const next = mockNext();
|
|
367
|
-
|
|
368
|
-
await createAuthContext(bothMethodsAuthOptions)([AuthMethods.JWT])(
|
|
369
|
-
req,
|
|
370
|
-
res,
|
|
371
|
-
next
|
|
372
|
-
);
|
|
373
|
-
|
|
374
|
-
expect(verifyJWT).toHaveBeenCalledTimes(0);
|
|
375
|
-
expect(res.status).toHaveBeenCalledWith(401);
|
|
376
|
-
expect(res.json).toHaveBeenCalledTimes(1);
|
|
377
|
-
expect(req.context.jwtUser).toBe(undefined);
|
|
378
|
-
expect(req.context.authenticated).toEqual([]);
|
|
379
|
-
expect(next).toHaveBeenCalledTimes(0);
|
|
380
|
-
});
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
describe("when api key doesnt exist", () => {
|
|
384
|
-
it("should not authenticate, should not call next, and it should return 401", async () => {
|
|
385
|
-
const req = mockRequest(undefined as any);
|
|
386
|
-
const res = mockResponse();
|
|
387
|
-
const next = mockNext();
|
|
388
|
-
|
|
389
|
-
await createAuthContext(bothMethodsAuthOptions)([
|
|
390
|
-
AuthMethods.API_KEY,
|
|
391
|
-
])(req, res, next);
|
|
392
|
-
|
|
393
|
-
expect(verifyJWT).toHaveBeenCalledTimes(0);
|
|
394
|
-
expect(res.status).toHaveBeenCalledWith(401);
|
|
395
|
-
expect(res.json).toHaveBeenCalledTimes(1);
|
|
396
|
-
expect(req.context.jwtUser).toBe(undefined);
|
|
397
|
-
expect(req.context.authenticated).toEqual([]);
|
|
398
|
-
expect(next).toHaveBeenCalledTimes(0);
|
|
399
|
-
});
|
|
400
|
-
});
|
|
401
|
-
});
|
|
402
|
-
});
|
|
403
|
-
});
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import { JWTUser, verifyJWT, VerifyOptions } from "@alanszp/jwt";
|
|
2
|
-
import { NextFunction, Response } from "express";
|
|
3
|
-
import { getRequestLogger } from "../helpers/getRequestLogger";
|
|
4
|
-
import { GenericRequest } from "../types/GenericRequest";
|
|
5
|
-
import { ILogger } from "@alanszp/logger";
|
|
6
|
-
import { compact, isEmpty, omit } from "lodash";
|
|
7
|
-
import { render401Error } from "../helpers/renderErrorJson";
|
|
8
|
-
|
|
9
|
-
function parseAuthorizationHeader(
|
|
10
|
-
authorization: string | undefined
|
|
11
|
-
): string | undefined {
|
|
12
|
-
if (!authorization) return undefined;
|
|
13
|
-
const [bearer, jwt, ...other] = authorization.split(" ");
|
|
14
|
-
|
|
15
|
-
if (bearer !== "Bearer" || other.length > 0) return undefined;
|
|
16
|
-
|
|
17
|
-
return jwt;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export enum AuthMethods {
|
|
21
|
-
JWT = "JWT",
|
|
22
|
-
API_KEY = "API_KEY",
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface JWTVerifyOptions extends Partial<VerifyOptions> {
|
|
26
|
-
publicKey: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface JWTOptions {
|
|
30
|
-
jwtVerifyOptions: JWTVerifyOptions;
|
|
31
|
-
types: [AuthMethods.JWT];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface ApiKeyOptions {
|
|
35
|
-
validApiKeys: string[];
|
|
36
|
-
types: [AuthMethods.API_KEY];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface BothMethodsOptions {
|
|
40
|
-
jwtVerifyOptions: JWTVerifyOptions;
|
|
41
|
-
validApiKeys: string[];
|
|
42
|
-
types:
|
|
43
|
-
| [AuthMethods.JWT, AuthMethods.API_KEY]
|
|
44
|
-
| [AuthMethods.API_KEY, AuthMethods.JWT];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export type AuthOptions = JWTOptions | ApiKeyOptions | BothMethodsOptions;
|
|
48
|
-
|
|
49
|
-
const middlewareGetterByAuthType: Record<
|
|
50
|
-
AuthMethods,
|
|
51
|
-
(
|
|
52
|
-
tokenOrJwt: string | null | undefined,
|
|
53
|
-
options: AuthOptions,
|
|
54
|
-
logger: ILogger
|
|
55
|
-
) => Promise<JWTUser | null | undefined>
|
|
56
|
-
> = {
|
|
57
|
-
[AuthMethods.JWT]: async (
|
|
58
|
-
jwt: string | null | undefined,
|
|
59
|
-
options: Exclude<AuthOptions, ApiKeyOptions>,
|
|
60
|
-
logger: ILogger
|
|
61
|
-
) => {
|
|
62
|
-
try {
|
|
63
|
-
if (!jwt) return undefined;
|
|
64
|
-
const jwtUser = await verifyJWT(
|
|
65
|
-
options.jwtVerifyOptions.publicKey,
|
|
66
|
-
jwt,
|
|
67
|
-
omit(options.jwtVerifyOptions, "publicKey")
|
|
68
|
-
);
|
|
69
|
-
logger.debug("auth.authWithJwt.authed", {
|
|
70
|
-
user: jwtUser.id,
|
|
71
|
-
org: jwtUser.organizationReference,
|
|
72
|
-
});
|
|
73
|
-
return jwtUser;
|
|
74
|
-
} catch (error: unknown) {
|
|
75
|
-
logger.info("auth.authWithJwt.invalidJwt", { jwt, error });
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
[AuthMethods.API_KEY]: async (
|
|
80
|
-
token: string | null | undefined,
|
|
81
|
-
options: Exclude<AuthOptions, JWTOptions>,
|
|
82
|
-
logger: ILogger
|
|
83
|
-
): Promise<JWTUser | null | undefined> => {
|
|
84
|
-
try {
|
|
85
|
-
if (!token) return undefined;
|
|
86
|
-
if (options.validApiKeys.includes(token)) {
|
|
87
|
-
logger.debug("auth.authWithApiKey.authed", {
|
|
88
|
-
user: "0",
|
|
89
|
-
org: "lara",
|
|
90
|
-
});
|
|
91
|
-
return Promise.resolve(
|
|
92
|
-
new JWTUser({
|
|
93
|
-
id: "0",
|
|
94
|
-
employeeReference: "0",
|
|
95
|
-
organizationReference: "lara",
|
|
96
|
-
roles: [],
|
|
97
|
-
segmentReference: null,
|
|
98
|
-
// This will be changed in the near future to grab all permissions.
|
|
99
|
-
permissions: "MA==", // 0 in base64
|
|
100
|
-
})
|
|
101
|
-
);
|
|
102
|
-
} else {
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
} catch (error: unknown) {
|
|
106
|
-
logger.info("auth.authWithApiKey.invalidApiKey", { token, error });
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
export function createAuthContext<Options extends AuthOptions>(
|
|
113
|
-
options: Options
|
|
114
|
-
) {
|
|
115
|
-
return function getMiddlewareForMethods(
|
|
116
|
-
authMethods: Options["types"][number][]
|
|
117
|
-
) {
|
|
118
|
-
return async function authWithGivenMethods(
|
|
119
|
-
req: GenericRequest,
|
|
120
|
-
res: Response,
|
|
121
|
-
next: NextFunction
|
|
122
|
-
): Promise<void> {
|
|
123
|
-
const logger = getRequestLogger(req);
|
|
124
|
-
const cookies = (req.cookies as Record<string, string | undefined>) || {};
|
|
125
|
-
const jwt =
|
|
126
|
-
cookies.jwt || parseAuthorizationHeader(req.headers.authorization);
|
|
127
|
-
|
|
128
|
-
try {
|
|
129
|
-
const authAttempts = await Promise.all(
|
|
130
|
-
authMethods.map((method) =>
|
|
131
|
-
middlewareGetterByAuthType[method](
|
|
132
|
-
method === AuthMethods.JWT ? jwt : req.headers.authorization,
|
|
133
|
-
options,
|
|
134
|
-
logger
|
|
135
|
-
)
|
|
136
|
-
)
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
const successfulAuthAttempts = compact(authAttempts);
|
|
140
|
-
|
|
141
|
-
if (isEmpty(successfulAuthAttempts)) {
|
|
142
|
-
res
|
|
143
|
-
.status(401)
|
|
144
|
-
.json(
|
|
145
|
-
render401Error([
|
|
146
|
-
authAttempts.includes(null)
|
|
147
|
-
? `Token invalid for methods ${authMethods}`
|
|
148
|
-
: `Token not set for methods ${authMethods}`,
|
|
149
|
-
])
|
|
150
|
-
);
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const jwtUser: JWTUser = successfulAuthAttempts[0];
|
|
155
|
-
req.context.jwtUser = jwtUser;
|
|
156
|
-
req.context.authenticated.push(
|
|
157
|
-
jwtUser.employeeReference !== "0" ? "jwt" : "api_key"
|
|
158
|
-
);
|
|
159
|
-
next();
|
|
160
|
-
} catch (error: unknown) {
|
|
161
|
-
logger.info("auth.authenticateUser.error", {
|
|
162
|
-
jwt,
|
|
163
|
-
token: req.headers.authorization,
|
|
164
|
-
methods: AuthMethods,
|
|
165
|
-
error,
|
|
166
|
-
});
|
|
167
|
-
res.status(401).json(render401Error(authMethods));
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
};
|
|
171
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { createContext } from "./createContext";
|
|
2
|
-
import { SharedContext } from "@alanszp/shared-context";
|
|
3
|
-
import { createMockLogger } from "@alanszp/logger";
|
|
4
|
-
import { createAuditLogger } from "@alanszp/audit";
|
|
5
|
-
import {
|
|
6
|
-
mockNext,
|
|
7
|
-
mockRequest,
|
|
8
|
-
mockRequestWithBody,
|
|
9
|
-
mockResponse,
|
|
10
|
-
} from "../test/mocks/expressMocks";
|
|
11
|
-
import { appIdentifier } from "@alanszp/core";
|
|
12
|
-
jest.mock("@alanszp/shared-context");
|
|
13
|
-
|
|
14
|
-
const logger = createMockLogger({});
|
|
15
|
-
const sharedContext = new SharedContext();
|
|
16
|
-
const lifecycleId = "123";
|
|
17
|
-
const lifecycleChain = "node:test";
|
|
18
|
-
|
|
19
|
-
describe("CreateContext", () => {
|
|
20
|
-
describe("when has lifecycle headers", () => {
|
|
21
|
-
beforeAll(() => {
|
|
22
|
-
(SharedContext as jest.Mock).mockClear();
|
|
23
|
-
});
|
|
24
|
-
it("should get lifecycle identifiers from there", () => {
|
|
25
|
-
const middleware = createContext(
|
|
26
|
-
sharedContext,
|
|
27
|
-
logger,
|
|
28
|
-
createAuditLogger(logger)
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
middleware(
|
|
32
|
-
mockRequest("authToken", {
|
|
33
|
-
"x-lifecycle-chain": lifecycleChain,
|
|
34
|
-
"x-lifecycle-id": lifecycleId,
|
|
35
|
-
}),
|
|
36
|
-
mockResponse(),
|
|
37
|
-
mockNext()
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
expect(sharedContext.run).toHaveBeenCalledWith(
|
|
41
|
-
expect.anything(),
|
|
42
|
-
expect.objectContaining({
|
|
43
|
-
lifecycleId,
|
|
44
|
-
lifecycleChain: `${lifecycleChain},${appIdentifier()}`,
|
|
45
|
-
})
|
|
46
|
-
);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe("when doesn't have lifecycle headers", () => {
|
|
51
|
-
beforeAll(() => {
|
|
52
|
-
(SharedContext as jest.Mock).mockClear();
|
|
53
|
-
});
|
|
54
|
-
it("should get lifecycle identifiers from body.detail", () => {
|
|
55
|
-
const middleware = createContext(
|
|
56
|
-
sharedContext,
|
|
57
|
-
logger,
|
|
58
|
-
createAuditLogger(logger)
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
middleware(
|
|
62
|
-
mockRequestWithBody(
|
|
63
|
-
"authToken",
|
|
64
|
-
{},
|
|
65
|
-
{
|
|
66
|
-
detail: {
|
|
67
|
-
lch: lifecycleChain,
|
|
68
|
-
lid: lifecycleId,
|
|
69
|
-
},
|
|
70
|
-
}
|
|
71
|
-
),
|
|
72
|
-
mockResponse(),
|
|
73
|
-
mockNext()
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
expect(sharedContext.run).toHaveBeenCalledWith(
|
|
77
|
-
expect.anything(),
|
|
78
|
-
expect.objectContaining({
|
|
79
|
-
lifecycleId,
|
|
80
|
-
lifecycleChain: `${lifecycleChain},${appIdentifier()}`,
|
|
81
|
-
})
|
|
82
|
-
);
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
});
|