diff --git a/README.md b/README.md index 268f96d..108ee03 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ The [`master`](https://github.com/codequest-eu/terraform-single-page-app/tree/ma | Name | Description | Type | Default | Required | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | :----: | :----------------: | :------: | +| basic\_auth\_credentials | Basic auth credentials in user:pass format | string | n/a | yes | | bucket | Kebab-cased bucket name override | string | `""` | no | | certificate\_arn | ACM certificate ARN to use instead of the default cloudfront certificate | string | `""` | no | | cloudfront\_price\_class | CloudFront price class, which specifies where the distribution should be replicated, one of: PriceClass_100, PriceClass_200, PriceClass_All | string | `"PriceClass_100"` | no | diff --git a/example/main.tf b/example/main.tf index ff26edd..2a7fdc5 100644 --- a/example/main.tf +++ b/example/main.tf @@ -20,8 +20,9 @@ module "basic" { } # only project name and environment are required - project = "terraform-spa" - environment = "example" + project = "terraform-spa-auth" + environment = "example" + basic_auth_credentials = "example:app" } resource "aws_s3_bucket_object" "basic_index" { diff --git a/main.tf b/main.tf index b58e828..99d3c1b 100644 --- a/main.tf +++ b/main.tf @@ -1,3 +1,7 @@ +provider "aws" { + alias = "middleware" +} + resource "aws_s3_bucket" "assets" { bucket = "${var.bucket != "" ? var.bucket : "${local.name_prefix}-assets"}" acl = "private" @@ -94,6 +98,12 @@ resource "aws_cloudfront_distribution" "assets" { forward = "none" } } + + lambda_function_association { + event_type = "viewer-request" + lambda_arn = "${module.basic_auth.arn}" + include_body = false + } } custom_error_response { @@ -118,3 +128,34 @@ resource "aws_cloudfront_distribution" "assets" { tags = "${local.tags}" } + +module "middleware_common" { + source = "./middleware_common" + + name_prefix = "${local.name_prefix}" + + providers = { + aws = "aws.middleware" + } +} + +data "template_file" "basic_auth" { + template = "${file("${path.module}/templates/basic-auth.js")}" + + vars { + credentials = "${base64encode("${var.basic_auth_credentials}")}" + } +} + +module "basic_auth" { + source = "./middleware" + + name = "${local.name_prefix}-basic-auth" + code = "${data.template_file.basic_auth.rendered}" + role_arn = "${module.middleware_common.role_arn}" + tags = "${local.tags}" + + providers = { + aws = "aws.middleware" + } +} diff --git a/templates/basic-auth.js b/templates/basic-auth.js new file mode 100644 index 0000000..316d1d2 --- /dev/null +++ b/templates/basic-auth.js @@ -0,0 +1,26 @@ +// Expected Authorization header value +const authorization = "Basic ${credentials}" + +exports.handler = (event, context, callback) => { + // Get request and request headers + const request = event.Records[0].cf.request + const headers = request.headers + + // Require Basic authentication + if ( + !headers.authorization || + headers.authorization[0].value !== authorization + ) { + return callback(null, { + status: "401", + statusDescription: "Unauthorized", + body: "Unauthorized", + headers: { + "www-authenticate": [{ key: "WWW-Authenticate", value: "Basic" }], + }, + }) + } + + // Continue request processing if authentication passed + callback(null, request) +} diff --git a/variables.tf b/variables.tf index 300a3fa..ee973c6 100644 --- a/variables.tf +++ b/variables.tf @@ -46,3 +46,7 @@ variable "bucket" { description = "Kebab-cased bucket name override" default = "" } + +variable "basic_auth_credentials" { + description = "Basic auth credentials in user:pass format" +}