Authentication on S3 Buckets

Authentication on S3 Buckets in AWS

Create AWS's version of a .htaccess file on a S3 hosted site. ...

Adding authentication on S3 Buckets is more tricky than adding it to a static websites via a .htaccess file, as this method is not compatible with S3 due to it being a file storage server (as opposed to a web server).

Luckily there is a working around for this which involves using lambda functions and Cloudfront CDN, which I will demonstrate in this post!

Step 1

Create a cloudfront CDN that is linked to a publically accessible S3 bucket (set to website hosting) that you’d like to put authentication on and create a lambda function in N. Virginia with the below code.

Step 2

Attach the lambda@edge access policy (see code below) to that lambda function, then click the ‘Deploy to Lambda@Edge’ using the actions button and ensure it is attached to the right CDN with the options ‘Viewer Request’ selected.

Step 3

Once the lambda has propagated to the CDN, visit the website and see the authentication appear – typing in the username + password you defined in the lambda function will let you access your S3 hosted website.

I’ve posted this scripts below (with comments) so you can now add authentication on S3 buckets in your web apps. I’ve also written a similar post on how to store data in an S3 bucket using lambda functions, you may find that useful too! Enjoy 😀

Lambda Function

exports.handler = (event, context, callback) => {

  // Get the request and its headers
  const request = event.Records[0].cf.request;
  const headers = request.headers;

  // Specify the username and password to be used
  const user = 'username';
  const pw = 'super-secret-password';

  // Build a Basic Authentication string
  const authString = 'Basic ' + new Buffer(user + ':' + pw).toString('base64');

  // Challenge for auth if auth credentials are absent or incorrect
  if (typeof headers.authorization == 'undefined' || headers.authorization[0].value != authString) {
    const response = {
      status: '401',
      statusDescription: 'Unauthorized',
      body: 'Unauthorized',
      headers: {
        'www-authenticate': [{key: 'WWW-Authenticate', value:'Basic'}]
      },
    };
    callback(null, response);
  }

  // User has authenticated
  callback(null, request);
};

Lambda permission role’s trust relationship

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "lambda.amazonaws.com",
          "edgelambda.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Share this post