- 
      
 - 
        
Save jeroenvollenbrock/94edbbc62adc986d6d6a9a3076e66f5b to your computer and use it in GitHub Desktop.  
| var USERS = { | |
| protecteddir: [{ | |
| username: 'user', | |
| password: 'pass', | |
| }], | |
| }; | |
| //Response when auth is not valid. | |
| var response401 = { | |
| statusCode: 401, | |
| statusDescription: 'Unauthorized', | |
| headers: { | |
| 'www-authenticate': {value:'Basic'}, | |
| }, | |
| }; | |
| var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; | |
| function btoa(input) { | |
| input = String(input); | |
| var bitmap, a, b, c, | |
| result = "", i = 0, | |
| rest = input.length % 3; // To determine the final padding | |
| for (; i < input.length;) { | |
| if ((a = input.charCodeAt(i++)) > 255 | |
| || (b = input.charCodeAt(i++)) > 255 | |
| || (c = input.charCodeAt(i++)) > 255) | |
| throw new TypeError("Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range."); | |
| bitmap = (a << 16) | (b << 8) | c; | |
| result += b64.charAt(bitmap >> 18 & 63) + b64.charAt(bitmap >> 12 & 63) | |
| + b64.charAt(bitmap >> 6 & 63) + b64.charAt(bitmap & 63); | |
| } | |
| // If there's need of padding, replace the last 'A's with equal signs | |
| return rest ? result.slice(0, rest - 3) + "===".substring(rest) : result; | |
| } | |
| function handler(event) { | |
| var request = event.request; | |
| var headers = request.headers; | |
| var auth = request.headers.authorization && request.headers.authorization.value; | |
| var project = request.uri.substring(1).split(/\.|\//)[0]; | |
| var users = USERS[project]; | |
| if(users) { | |
| if(!auth || !auth.startsWith('Basic ')) { | |
| return response401; | |
| } | |
| if(!users.find(function(user) { | |
| // Construct the Basic Auth string | |
| var authString = 'Basic ' + btoa(user.username + ':' + user.password); | |
| return authString === auth; | |
| })) { | |
| return response401; | |
| } | |
| } | |
| return request; | |
| } | 
Can confirm this works like a charm
Can confirm this works like a charm
I created the cloudfront funtion based on guide as below, but basic authen not work for me. Can you share with me how can you do it?
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-tutorial.html
Sure! I modified it somewhat to fit our purposes, but this works:
Cloudformation stack for the function:
AWSTemplateFormatVersion: 2010-09-09
Description: This will create basic auth to protect multiple CDN's
Resources:
    cloudfrontAuth:
      Type: AWS::CloudFront::Function
      Properties:
        Name: !Sub ${AWS::StackName}-basicAuth
        AutoPublish: true
        FunctionConfig:
          Comment: !Sub ${AWS::StackName}-basicAuth
          Runtime: cloudfront-js-1.0
        FunctionCode: |
          // List of users.
          var users = [
            {
              username: 'thename',
              password: 'thepassword',
            }
          ];
          // List of IP (v4)
          var ip = [
            '1.2.3.4', // some IP addresses that get direct access
          ];
          // Response when auth is not valid.
          var response401 = {
              statusCode: 401,
              statusDescription: 'Unauthorized',
              headers: {
                  'www-authenticate': {value:'Basic'},
              },
          };
          // Because we cannot import.
          var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
          function btoa(input) {
                  input = String(input);
                  var bitmap, a, b, c,
                      result = "", i = 0,
                      rest = input.length % 3; // To determine the final padding
                  for (; i < input.length;) {
                      if ((a = input.charCodeAt(i++)) > 255
                              || (b = input.charCodeAt(i++)) > 255
                              || (c = input.charCodeAt(i++)) > 255)
                          throw new TypeError("Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.");
                      bitmap = (a << 16) | (b << 8) | c;
                      result += b64.charAt(bitmap >> 18 & 63) + b64.charAt(bitmap >> 12 & 63)
                              + b64.charAt(bitmap >> 6 & 63) + b64.charAt(bitmap & 63);
                  }
                  // If there's need of padding, replace the last 'A's with equal signs
                  return rest ? result.slice(0, rest - 3) + "===".substring(rest) : result;
              }
          // Main loop
          function handler(event) {
              var request = event.request;
              // IP Access.
              if (ip.find(function(ipA) {
                return ipA === event.viewer.ip;
              })) {
                return request;
              }
              // User Access.
              var auth = request.headers.authorization && request.headers.authorization.value;
              if (!auth || !auth.startsWith('Basic ')) {
                return response401;
              }
              if (!users.find(function(user) {
                // Construct the Basic Auth string
                var authString = 'Basic ' + btoa(user.username + ':' + user.password);
                return authString === auth;
              })) {
                return response401;
              }
              return request;
          }
Outputs:
  cloudfrontAuthFunctionARN:
    Value: !GetAtt cloudfrontAuth.FunctionMetadata.FunctionARN
    Export:
      Name: !Sub ${AWS::StackName}-cloudfrontAuthFunctionARNThen how I clicked it into a cloudfront distribution:
...
  DefaultCacheBehavior:
     ....
            FunctionAssociations:
              - EventType: viewer-request
                FunctionARN:
                  Fn::ImportValue: <ARN-OF-YOUR-FUNCTION>Thank you so much for your support, It's working for me now !!!
yw!
Thanks for this!
I adapted it slightly because I wanted a single user for the whole site.
Coworker @aalin also simplified it a little.
We set it up per @lehuy2012's comment above: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-tutorial.html
Edited 2022-10-02 to handle ":" in passwords per comments below.
// Source: https://gist.github.com/jeroenvollenbrock/94edbbc62adc986d6d6a9a3076e66f5b
var USERNAME = 'myuser';
var PASSWORD = 'mypassword';
var response401 = {
  statusCode: 401,
  statusDescription: 'Unauthorized',
  headers: {
    'www-authenticate': {value:'Basic'},
  },
};
function validateBasicAuth(authHeader) {
  var match = authHeader.match(/^Basic (.+)$/);
  if (!match) return false;
  var credentials = String.bytesFrom(match[1], 'base64').split(':', 2);
  return credentials[0] === USERNAME && credentials[1] === PASSWORD;
}
function handler(event) {
  var request = event.request;
  var headers = request.headers;
  var auth = (headers.authorization && headers.authorization.value) || '';
  if (!validateBasicAuth(auth)) return response401;
  return request;
}@henrik does your example hold up if the password itself contains :?
@rashidnhm: It seems like it doesn't.
It should only split the credentials into two parts. This should work with passwords containing :.
var credentials = String.bytesFrom(match[1], 'base64').split(':', 2);Thanks @rashidnhm – good catch! And thanks @aalin :)
I've edited my comment above to incorporate @aalin's fix for ease of copy-pasting.
And to make it explicit – we only need to handle ":" in passwords, not in usernames. They're not allowed in usernames.
Using the function above generated the following error:
The CloudFront function associated with the CloudFront distribution is invalid or could not run. SyntaxError: String.bytesFrom() is deprecated, please use another method Buffer.from()
This is the adjustment which made it work:
  const credentials = Buffer.from(match[1], 'base64').toString('utf-8').split(':', 2);
    
Hi there, have you checked it with cloudfront function on AWS? thank you :))