Skip to content

Commit 41d85f4

Browse files
authored
Use testing credentials secret from AWS for JWT key (#176)
1 parent d68d9ee commit 41d85f4

21 files changed

+80
-35
lines changed

cloudformation/iam.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Resources:
4343
Effect: Allow
4444
Resource:
4545
- Fn::Sub: arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:infra-core-api-config*
46+
- Fn::Sub: arn:aws:secretsmanager:${AWS::Region}:427040638965:secret:infra-core-api-testing-credentials* # this secret only exists in awsdev account
4647

4748
- Action:
4849
- dynamodb:DescribeLimits

generate_jwt.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const getSecretValue = async (secretId) => {
2020
}
2121
};
2222

23-
const secrets = await getSecretValue("infra-core-api-config");
23+
const secrets = await getSecretValue("infra-core-api-testing-credentials");
2424
const client = new STSClient({ region: "us-east-1" });
2525
const command = new GetCallerIdentityCommand({});
2626
let data;

src/api/index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
environmentConfig,
1616
genericConfig,
1717
SecretConfig,
18+
SecretTesting,
1819
} from "../common/config.js";
1920
import organizationsPlugin from "./routes/organizations.js";
2021
import authorizeFromSchemaPlugin from "./plugins/authorizeFromSchema.js";
@@ -252,13 +253,20 @@ async function init(prettyPrint: boolean = false) {
252253
app.dynamoClient = dynamoClient;
253254
app.secretsManagerClient = secretsManagerClient;
254255
app.redisClient = redisClient;
255-
app.secretConfig = secret;
256256
app.refreshSecretConfig = async () => {
257257
app.secretConfig = (await getSecretValue(
258258
app.secretsManagerClient,
259259
genericConfig.ConfigSecretName,
260260
)) as SecretConfig;
261+
if (app.environmentConfig.TestingCredentialsSecret) {
262+
const temp = (await getSecretValue(
263+
app.secretsManagerClient,
264+
app.environmentConfig.TestingCredentialsSecret,
265+
)) as SecretTesting;
266+
app.secretConfig = { ...app.secretConfig, ...temp };
267+
}
261268
};
269+
app.refreshSecretConfig();
262270
app.addHook("onRequest", (req, _, done) => {
263271
req.startTime = now();
264272
const hostname = req.hostname;

src/api/lambda.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import "zod-openapi/extend";
2-
import awsLambdaFastify from "@fastify/aws-lambda";
2+
import awsLambdaFastify, { LambdaResponse } from "@fastify/aws-lambda";
33
import init from "./index.js";
44
import warmer from "lambda-warmer";
55
import { type APIGatewayEvent, type Context } from "aws-lambda";
6+
import { InternalServerError } from "common/errors/index.js";
67

78
const app = await init();
89
const realHandler = awsLambdaFastify(app, {
@@ -16,7 +17,21 @@ const handler = async (event: APIGatewayEvent, context: Context) => {
1617
return "warmed";
1718
}
1819
// else proceed with handler logic
19-
return realHandler(event, context);
20+
return realHandler(event, context).catch((e) => {
21+
console.error(e);
22+
const newError = new InternalServerError({
23+
message: "Failed to initialize application.",
24+
});
25+
const json = JSON.stringify(newError.toJson());
26+
return {
27+
statusCode: newError.httpStatusCode,
28+
body: json,
29+
headers: {
30+
"Content-Type": "application/json",
31+
},
32+
isBase64Encoded: false,
33+
};
34+
});
2035
};
2136

2237
await app.ready(); // needs to be placed after awsLambdaFastify call because of the decoration: https://github.com/fastify/aws-lambda-fastify/blob/master/index.js#L9

src/api/plugins/auth.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
UnauthenticatedError,
1414
UnauthorizedError,
1515
} from "../../common/errors/index.js";
16-
import { SecretConfig } from "../../common/config.js";
16+
import { SecretConfig, SecretTesting } from "../../common/config.js";
1717
import {
1818
AUTH_DECISION_CACHE_SECONDS,
1919
getGroupRoles,
@@ -195,7 +195,7 @@ const authPlugin: FastifyPluginAsync = async (fastify, _options) => {
195195
}
196196
signingKey =
197197
process.env.JwtSigningKey ||
198-
(fastify.secretConfig.jwt_key as string) ||
198+
((fastify.secretConfig as SecretTesting).jwt_key as string) ||
199199
"";
200200
if (signingKey === "") {
201201
throw new UnauthenticatedError({

src/api/types.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { FastifyRequest, FastifyInstance, FastifyReply } from "fastify";
33
import { AppRoles, RunEnvironment } from "../common/roles.js";
44
import { AadToken } from "./plugins/auth.js";
5-
import { ConfigType, SecretConfig } from "../common/config.js";
5+
import { ConfigType, SecretConfig, SecretTesting } from "../common/config.js";
66
import NodeCache from "node-cache";
77
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
88
import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
@@ -36,7 +36,7 @@ declare module "fastify" {
3636
redisClient: Redis;
3737
secretsManagerClient: SecretsManagerClient;
3838
cloudfrontKvClient: CloudFrontKeyValueStoreClient;
39-
secretConfig: SecretConfig;
39+
secretConfig: SecretConfig | (SecretConfig & SecretTesting);
4040
refreshSecretConfig: CallableFunction;
4141
}
4242
interface FastifyRequest {

src/common/config.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export type ConfigType = {
2323
PaidMemberPriceId: string;
2424
AadValidReadOnlyClientId: string;
2525
LinkryCloudfrontKvArn?: string;
26+
TestingCredentialsSecret?: string;
2627
};
2728

2829
export type GenericConfigType = {
@@ -98,6 +99,7 @@ const environmentConfig: EnvironmentConfigType = {
9899
/^https:\/\/(?:.*\.)?acmuiuc\.pages\.dev$/,
99100
/http:\/\/localhost:\d+$/,
100101
],
102+
TestingCredentialsSecret: "infra-core-api-testing-credentials",
101103
AadValidClientId: "39c28870-94e4-47ee-b4fb-affe0bf96c9f",
102104
LinkryBaseUrl: "https://core.aws.qa.acmuiuc.org",
103105
PasskitIdentifier: "pass.org.acmuiuc.qa.membership",
@@ -137,7 +139,6 @@ const environmentConfig: EnvironmentConfigType = {
137139
};
138140

139141
export type SecretConfig = {
140-
jwt_key?: string;
141142
discord_guild_id: string;
142143
discord_bot_token: string;
143144
entra_id_private_key?: string;
@@ -151,6 +152,10 @@ export type SecretConfig = {
151152
redis_url: string;
152153
};
153154

155+
export type SecretTesting = {
156+
jwt_key: string;
157+
}
158+
154159
const roleArns = {
155160
Entra: process.env.EntraRoleArn,
156161
};

tests/e2e/base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ async function getSecrets() {
3030
let response = { PLAYWRIGHT_USERNAME: "", PLAYWRIGHT_PASSWORD: "" };
3131
let keyData;
3232
if (!process.env.PLAYWRIGHT_USERNAME || !process.env.PLAYWRIGHT_PASSWORD) {
33-
keyData = await getSecretValue("infra-core-api-config");
33+
keyData = await getSecretValue("infra-core-api-testing-credentials");
3434
}
3535
response["PLAYWRIGHT_USERNAME"] =
3636
process.env.PLAYWRIGHT_USERNAME ||

tests/live/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ async function getSecrets() {
3030
const response = { JWTKEY: "" };
3131
let keyData;
3232
if (!process.env.JWT_KEY) {
33-
keyData = await getSecretValue("infra-core-api-config");
33+
keyData = await getSecretValue("infra-core-api-testing-credentials");
3434
}
3535
response["JWTKEY"] =
3636
process.env.JWT_KEY || ((keyData ? keyData["jwt_key"] : "") as string);

tests/unit/apiKey.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { afterAll, expect, test, beforeEach, vi, describe } from "vitest";
22
import { mockClient } from "aws-sdk-client-mock";
33
import init from "../../src/api/index.js";
44
import { createJwt } from "./auth.test.js";
5-
import { secretObject } from "./secret.testdata.js";
5+
import { testSecretObject } from "./secret.testdata.js";
66
import supertest from "supertest";
77
import {
88
ConditionalCheckFailedException,
@@ -31,7 +31,7 @@ vi.mock("../../src/api/functions/apiKey.js", () => {
3131
// Mock DynamoDB client
3232
const dynamoMock = mockClient(DynamoDBClient);
3333
const sqsMock = mockClient(SQSClient);
34-
const jwt_secret = secretObject["jwt_key"];
34+
const jwt_secret = testSecretObject["jwt_key"];
3535

3636
vi.stubEnv("JwtSigningKey", jwt_secret);
3737

0 commit comments

Comments
 (0)