Skip to content

Commit

Permalink
808: Authenticate backend via cloudfront function (#904)
Browse files Browse the repository at this point in the history
* Prevent unauthorized API access using a CloudFront proxy
* Make UI auth opt-in
* Correct document loading and cache control
  • Loading branch information
chriswilty committed Sep 25, 2024
1 parent 7917076 commit e93eac5
Show file tree
Hide file tree
Showing 38 changed files with 4,248 additions and 1,395 deletions.
1 change: 1 addition & 0 deletions .github/workflows/backend-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
branches:
- main
- dev
- 'feature/**'
paths:
- 'backend/**'

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/frontend-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
branches:
- main
- dev
- 'feature/**'
paths:
- 'frontend/**'

Expand Down
1 change: 0 additions & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
FROM node:lts-alpine
ENV NODE_ENV=production

WORKDIR /usr/app
COPY package*.json ./
Expand Down
4 changes: 1 addition & 3 deletions backend/src/server/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import nonSessionRoutes from './nonSessionRoutes';
import sessionRoutes from './sessionRoutes';
import uiRoutes from './uiRoutes';

const app = express()
.use(express.json())
.use(queryTypes.middleware());
const app = express().use(express.json()).use(queryTypes.middleware());

const isDevelopment = env.NODE_ENV === 'development';
const isServingUI = env.NODE_ENV === 'prodlite';
Expand Down
17 changes: 10 additions & 7 deletions backend/src/server/proxySetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,14 @@ function parseForwardedHeader(request: Request) {
?.split(',')
.at(0)
?.split(';')
.reduce((result, proxyProps) => {
const [key, value] = proxyProps.split('=');
if (key && value) {
result[key] = value;
}
return result;
}, {} as Record<string, string>);
.reduce(
(result, proxyProps) => {
const [key, value] = proxyProps.split('=');
if (key && value) {
result[key] = value;
}
return result;
},
{} as Record<string, string>
);
}
11 changes: 0 additions & 11 deletions backend/src/server/sessionRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,6 @@ router.use((req, _res, next) => {
next();
});

// TODO: Remove this debug logging!
if (isProduction) {
router.use('/openai', (req, res, next) => {
console.log('Request:', req.path, `secure=${req.secure}`, req.headers);
res.on('finish', () => {
console.log('Response:', req.path, res.getHeaders());
});
next();
});
}

// handshake
router.get('/start', handleStart);

Expand Down
2 changes: 2 additions & 0 deletions cloud/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DOMAIN_NAME=your.domain
HOSTED_ZONE_ID=YOUR_AWS_HOSTED_ZONE_ID
29 changes: 27 additions & 2 deletions cloud/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ module.exports = {
'plugin:@typescript-eslint/recommended-type-checked',
'plugin:@typescript-eslint/stylistic-type-checked',
'plugin:@typescript-eslint/strict-type-checked',
'plugin:import/recommended',
'plugin:import/typescript',
'prettier',
],
parser: '@typescript-eslint/parser',
parserOptions: {
Expand All @@ -15,6 +18,23 @@ module.exports = {
root: true,
ignorePatterns: ['node_modules', 'cdk.out'],
rules: {
eqeqeq: 'error',
'func-style': ['error', 'expression', { allowArrowFunctions: true }],
'object-shorthand': ['error', 'always', { avoidExplicitReturnArrows: true }],
'prefer-template': 'error',
'import/order': [
'error',
{
alphabetize: { order: 'asc' },
'newlines-between': 'always',
warnOnUnassignedImports: true,
groups: [
['builtin', 'external'],
['internal', 'parent', 'index', 'sibling'],
['object', 'type'],
],
},
],
'@typescript-eslint/consistent-type-definitions': ['error', 'type'],
'@typescript-eslint/init-declarations': 'error',
'@typescript-eslint/no-misused-promises': [
Expand All @@ -23,8 +43,13 @@ module.exports = {
checksVoidReturn: false,
},
],
'@typescript-eslint/restrict-template-expressions': [
'error',
{
allowNumber: true,
allowBoolean: true,
},
],
'@typescript-eslint/unbound-method': ['error', { ignoreStatic: true }],
'prefer-template': 'error',
eqeqeq: 'error',
},
};
4 changes: 4 additions & 0 deletions cloud/.prettierrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
printWidth: 100
singleQuote: true
trailingComma: 'es5'
useTabs: true
62 changes: 29 additions & 33 deletions cloud/bin/cloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import 'source-map-support/register';

import {
appName,
environmentName,
resourceDescription,
stackName,
stageName,
ApiStack,
AuthStack,
CertificateStack,
Expand All @@ -27,57 +27,53 @@ const env: Environment = {

const tags = {
owner: appName,
classification: 'unrestricted',
'environment-type': environmentName(app),
'keep-alive': '9-5-without-weekends',
stage: stageName(app),
};

/* Stack constructs */

const hostedZoneStack = new HostedZoneStack(
app,
generateStackName('hosted-zone'),
{
description: generateDescription('Hosted Zone stack'),
env,
tags,
}
);

const certificateStack = new CertificateStack(
app,
generateStackName('certificate'),
{
description: generateDescription('Certificate stack'),
env,
tags,
hostedZone: hostedZoneStack.hostedZone,
}
);
const hostedZoneStack = new HostedZoneStack(app, generateStackName('hosted-zone'), {
description: generateDescription('Hosted Zone stack'),
env,
tags,
});

const uiStack = new UiStack(app, generateStackName('ui'), {
description: generateDescription('UI stack'),
const certificateStack = new CertificateStack(app, generateStackName('certificate'), {
description: generateDescription('Certificate stack'),
env,
tags,
certificate: certificateStack.cloudFrontCert,
hostedZone: hostedZoneStack.hostedZone,
});

/*const authStack = */ new AuthStack(app, generateStackName('auth'), {
const authStack = new AuthStack(app, generateStackName('auth'), {
description: generateDescription('Auth stack'),
env,
tags,
webappUrl: uiStack.cloudFrontUrl,
authDomainName: certificateStack.authDomainName,
certificate: certificateStack.cloudFrontCert,
hostedZone: hostedZoneStack.hostedZone,
});

new ApiStack(app, generateStackName('api'), {
description: generateDescription('API stack'),
env,
tags,
apiDomainName: certificateStack.apiDomainName,
certificate: certificateStack.loadBalancerCert,
customAuthHeaderName: authStack.customAuthHeaderName,
customAuthHeaderValue: authStack.customAuthHeaderValue,
hostedZone: hostedZoneStack.hostedZone,
});

new UiStack(app, generateStackName('ui'), {
description: generateDescription('UI stack'),
env,
tags,
apiDomainName: certificateStack.apiDomainName,
certificate: certificateStack.cloudFrontCert,
customAuthHeaderName: authStack.customAuthHeaderName,
customAuthHeaderValue: authStack.customAuthHeaderValue,
hostedZone: hostedZoneStack.hostedZone,
// userPool: authStack.userPool,
// userPoolClient: authStack.userPoolClient,
// userPoolDomain: authStack.userPoolDomain,
webappUrl: uiStack.cloudFrontUrl,
parameterNameUserPoolClient: authStack.parameterNameUserPoolClient,
parameterNameUserPoolId: authStack.parameterNameUserPoolId,
});
2 changes: 1 addition & 1 deletion cloud/cdk.context.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
"eu-north-1b",
"eu-north-1c"
],
"ami:account=992382568770:filters.image-type.0=machine:filters.name.0=amzn-ami-vpc-nat-*:filters.state.0=available:owners.0=amazon:region=eu-north-1": "ami-072517490bf2cf3a3"
"acknowledged-issue-numbers": [29483]
}
3 changes: 2 additions & 1 deletion cloud/cdk.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
"@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true
}
}
Loading

0 comments on commit e93eac5

Please sign in to comment.