aws4-axios
Version:
Axios request interceptor for signing requests with AWSv4
243 lines (242 loc) • 10.3 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const client_sts_1 = require("@aws-sdk/client-sts");
const axios_1 = __importDefault(require("axios"));
const __1 = require("..");
const methods = ["GET", "DELETE"];
const dataMethods = ["POST", "PATCH", "PUT"];
const region = process.env.AWS_REGION;
const apiGateway = process.env.API_GATEWAY_URL;
const clientRoleArn = process.env.CLIENT_ROLE_ARN;
const assumedClientRoleArn = process.env.ASSUMED_CLIENT_ROLE_ARN;
const service = "execute-api";
let clientCredentials;
beforeAll(() => __awaiter(void 0, void 0, void 0, function* () {
const sts = new client_sts_1.STSClient({ region });
const { Credentials: credentials } = yield sts.send(new client_sts_1.AssumeRoleCommand({
RoleArn: clientRoleArn,
RoleSessionName: "integration-tests",
}));
clientCredentials = {
accessKeyId: (credentials === null || credentials === void 0 ? void 0 : credentials.AccessKeyId) || "",
secretAccessKey: (credentials === null || credentials === void 0 ? void 0 : credentials.SecretAccessKey) || "",
sessionToken: (credentials === null || credentials === void 0 ? void 0 : credentials.SessionToken) || "",
};
cleanEnvCredentials();
}));
const setEnvCredentials = () => {
process.env.AWS_ACCESS_KEY_ID = clientCredentials === null || clientCredentials === void 0 ? void 0 : clientCredentials.accessKeyId;
process.env.AWS_SECRET_ACCESS_KEY = clientCredentials === null || clientCredentials === void 0 ? void 0 : clientCredentials.secretAccessKey;
process.env.AWS_SESSION_TOKEN = clientCredentials === null || clientCredentials === void 0 ? void 0 : clientCredentials.sessionToken;
};
const cleanEnvCredentials = () => {
delete process.env.AWS_PROFILE;
delete process.env.AWS_ACCESS_KEY_ID;
delete process.env.AWS_SECRET_ACCESS_KEY;
delete process.env.AWS_SESSION_TOKEN;
};
describe("check that API is actually protected", () => {
it.each([...methods, ...dataMethods])("checks that HTTP %s is protected", (method) => __awaiter(void 0, void 0, void 0, function* () {
yield expect(axios_1.default.request({ url: apiGateway, method })).rejects.toMatchObject({
response: {
status: 403,
},
});
}));
});
describe("with credentials from environment variables", () => {
let client;
const data = {
foo: "bar",
};
beforeAll(() => {
setEnvCredentials();
});
afterAll(() => {
cleanEnvCredentials();
});
beforeEach(() => {
client = axios_1.default.create();
client.interceptors.request.use((0, __1.aws4Interceptor)({ options: { region, service }, instance: client }));
});
it.each(methods)("HTTP %s", (method) => __awaiter(void 0, void 0, void 0, function* () {
let error;
let result;
try {
result = yield client.request({ url: apiGateway, method });
}
catch (err) {
error = (0, __1.getAuthErrorMessage)(err);
}
expect(error).toBe(undefined);
expect(result === null || result === void 0 ? void 0 : result.status).toEqual(200);
expect(result && result.data.requestContext.http.method).toBe(method);
expect(result && result.data.requestContext.http.path).toBe("/");
}));
it.each(dataMethods)("HTTP %s", (method) => __awaiter(void 0, void 0, void 0, function* () {
let error;
let result;
try {
result = yield client.request({
url: apiGateway,
method,
data,
headers: { foo: "bar" },
});
}
catch (err) {
error = (0, __1.getAuthErrorMessage)(err);
}
expect(error).toBe(undefined);
expect(result === null || result === void 0 ? void 0 : result.status).toEqual(200);
expect(result === null || result === void 0 ? void 0 : result.data.requestContext.http.method).toBe(method);
expect(result === null || result === void 0 ? void 0 : result.data.requestContext.http.path).toBe("/");
expect(result && JSON.parse(result.data.body)).toStrictEqual(data);
expect(result === null || result === void 0 ? void 0 : result.data.headers.foo).toEqual("bar");
}));
it("handles path", () => __awaiter(void 0, void 0, void 0, function* () {
let error;
let result;
try {
result = yield client.request({
url: apiGateway + "/some/path",
});
}
catch (err) {
error = (0, __1.getAuthErrorMessage)(err);
}
expect(error).toBe(undefined);
expect(result === null || result === void 0 ? void 0 : result.status).toEqual(200);
expect(result && result.data.requestContext.http.path).toBe("/some/path");
}));
it("handles query parameters", () => __awaiter(void 0, void 0, void 0, function* () {
let error;
let result;
try {
result = yield client.request({
url: apiGateway,
params: {
lorem: 42,
},
});
}
catch (err) {
error = (0, __1.getAuthErrorMessage)(err);
}
expect(error).toBe(undefined);
expect(result === null || result === void 0 ? void 0 : result.status).toEqual(200);
expect(result && result.data.rawQueryString).toBe("lorem=42");
}));
it("handles custom headers", () => __awaiter(void 0, void 0, void 0, function* () {
let error;
let result;
try {
result = yield client.request({
url: apiGateway,
method: "POST",
headers: { "X-Custom-Header": "Baz" },
data,
});
}
catch (err) {
error = (0, __1.getAuthErrorMessage)(err);
}
expect(error).toBe(undefined);
expect(result === null || result === void 0 ? void 0 : result.status).toEqual(200);
expect(result === null || result === void 0 ? void 0 : result.data.headers["x-custom-header"]).toBe("Baz");
}));
it("handles custom Content-Type header", () => __awaiter(void 0, void 0, void 0, function* () {
let error;
let result;
try {
result = yield client.request({
url: apiGateway,
method: "POST",
headers: { "Content-Type": "application/xml" },
data,
});
}
catch (err) {
error = (0, __1.getAuthErrorMessage)(err);
}
expect(error).toBe(undefined);
expect(result === null || result === void 0 ? void 0 : result.status).toEqual(200);
expect(result === null || result === void 0 ? void 0 : result.data.headers["content-type"]).toBe("application/xml");
}));
it("sets content type as application/json when the body is an object", () => __awaiter(void 0, void 0, void 0, function* () {
let error;
let result;
try {
result = yield client.request({
url: apiGateway,
method: "POST",
data,
});
}
catch (err) {
error = (0, __1.getAuthErrorMessage)(err);
}
expect(error).toBe(undefined);
expect(result === null || result === void 0 ? void 0 : result.status).toEqual(200);
expect(result === null || result === void 0 ? void 0 : result.data.headers["content-type"]).toBe("application/json");
}));
});
describe("signQuery", () => {
beforeAll(() => {
setEnvCredentials();
});
afterAll(() => {
cleanEnvCredentials();
});
it("respects signQuery option", () => __awaiter(void 0, void 0, void 0, function* () {
const client = axios_1.default.create();
client.interceptors.request.use((0, __1.aws4Interceptor)({
instance: client,
options: {
region,
service,
signQuery: true,
},
}));
const result = yield client.request({
url: apiGateway + "/some/path",
method: "GET",
params: { foo: "bar" },
});
expect(result === null || result === void 0 ? void 0 : result.status).toEqual(200);
}));
});
describe("with role to assume", () => {
let client;
const assumedRoleName = assumedClientRoleArn === null || assumedClientRoleArn === void 0 ? void 0 : assumedClientRoleArn.substr(assumedClientRoleArn.indexOf("/") + 1);
beforeAll(() => {
setEnvCredentials();
});
afterAll(() => {
cleanEnvCredentials();
});
beforeEach(() => {
client = axios_1.default.create();
client.interceptors.request.use((0, __1.aws4Interceptor)({
options: { region, service, assumeRoleArn: assumedClientRoleArn },
instance: client,
}));
});
it.each([...methods, ...dataMethods])("signs HTTP %s request with assumed role credentials", (method) => __awaiter(void 0, void 0, void 0, function* () {
const result = yield client.request({ url: apiGateway, method });
expect(result === null || result === void 0 ? void 0 : result.status).toEqual(200);
expect(result && result.data.requestContext.authorizer.iam.userArn).toContain("/" + assumedRoleName + "/");
}));
});