Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Amazon authentication v4 support #1128

Closed
karensg opened this issue Nov 10, 2015 · 19 comments
Closed

Amazon authentication v4 support #1128

karensg opened this issue Nov 10, 2015 · 19 comments

Comments

@karensg
Copy link

karensg commented Nov 10, 2015

First of all, lots of compliments for your great job!

Would it be possible to update the version of amazon authentication to v4 and update the code example for it. At this moment, all Amazon regions support v4. However, Frankfurt is the first region that does not support v2 any more. It would be great to use this library for those buckets as well.

Cheers,
Karens

@danialfarid
Copy link
Owner

Are you sure you are on the current github?

@karensg
Copy link
Author

karensg commented Nov 11, 2015

If I am correct, the signing in the example and in the demo happens with v2.
More on signing with v4

@danialfarid
Copy link
Owner

Could you submit a PR?

@karensg
Copy link
Author

karensg commented Nov 20, 2015

I am not sure I will be able to do that. Just a suggestion that it will be more important in the future

@Hnus
Copy link

Hnus commented Dec 19, 2015

+1 Does not work with Frankfurt

@bourgeois247
Copy link

For those who want or need to use version 4 authentication for AWS S3 uploads, just change the data attributes given in the ng-file-upload documentation to the v4 parameters. These are the parameters you need to consider:

  1. key // the key to store the file on S3, could be file name or customized
  2. acl, // sets the access to the uploaded file in the bucket: private, public-read, .
  3. policy //as described here
  4. 'X-amz-credential' //also described in the link above
  5. 'X-amz-algorithm': 'AWS4-HMAC-SHA256'
  6. 'X-amz-date': //as described in the link above
  7. 'X-amz-expires': //as described in the link above
  8. 'X-amz-signature': //as described in the link above
  9. 'Content-Type': file.type !== '' ? file.type : 'application/octet-stream', // content type of the file (NotEmpty)
  10. file: file //the actual file object to be uploaded

You don't need to get a signed url, you can use the format [YOUR_BUCKET_NAME].s3.amazonaws.com. The policy and signature parameters are to be generated on the server. A sample Php implementation of generating those can be found here.

Hope that helps someone, cheers.

@danialfarid
Copy link
Owner

@bourgeois247 Thanks, If you could create a wiki page with code sample would be great. I have already added a link to this comment in the readme but a wiki would be much better.

@bourgeois247
Copy link

@danialfarid sure, will do

@notbrain
Copy link

@bourgeois247 Do you have a working example with AWS V4 auth somewhere? Would greatly appreciate any pointers, haven't been successful.

@flosky
Copy link

flosky commented May 2, 2016

Hey guys,
I was strugling with that as well. Especially because this is only needed in two regions, so the support is not that great.
This is my source code (with a node.js backend). Notice that the signature must be hex encoded (took me a while to figure that out)

    app.use('/sign_s3', function(req, res) {

        const date = new Date().toISOString().replace(/[\.\-:]/gi, "").substr(0, 15) + "Z";
        const dateNowRaw = date.substr(0, date.indexOf("T"));

        const expirationDate = new Date();
        expirationDate.setHours(expirationDate.getHours() + 1);
        const expiration = expirationDate.toISOString();

        const credentials = '<myAwsId>/' + dateNowRaw + '/eu-central-1/s3/aws4_request';
        const policy = {
            expiration: expiration,
            conditions: [
                {"bucket": "<myBucketName>"},
                {"acl": "private"},
                ["starts-with", "$key", ''],
                ['starts-with', '$Content-Type', ''],
                {"x-amz-credential": credentials},
                {"x-amz-algorithm": "AWS4-HMAC-SHA256"},
                {"x-amz-date": date}
            ]
        };

        const base64Policy = new Buffer(JSON.stringify(policy), "utf-8").toString("base64");

        const dateKey = crypto.createHmac('sha256', "AWS4" + "<myPrivateAwsKey>").update(dateNowRaw).digest();
        const dateRegionKey = crypto.createHmac('sha256', dateKey).update('eu-central-1').digest();
        const dateRegionServiceKey = crypto.createHmac('sha256', dateRegionKey).update('s3').digest();
        const signingKey = crypto.createHmac('sha256', dateRegionServiceKey).update('aws4_request').digest();

        const signature = crypto.createHmac('sha256', signingKey).update(base64Policy).digest('hex');

        res.status(200).json({
            signature: signature,
            policy: base64Policy,
            date: date,
            credentials: credentials,
            expiration: expiration
        });
    });

On the client:

            // make request to your own backend to get signature and other data
            var signature = response.data.signature;
            var policyBase64 = response.data.policy;
            var date = response.data.date;
            var credentials = response.data.credentials;
            var expiration = response.data.expiration;

            Upload.upload({
                url: 'https://<myBucketName>.s3.amazonaws.com/',
                method: 'POST',
                data: {
                    key: file.name,
                    acl: 'private',
                    Policy: policyBase64,
                    'X-Amz-Credential' : credentials,
                    'X-Amz-Algorithm': 'AWS4-HMAC-SHA256',
                    'X-Amz-Date': date,
                    'X-Amz-Signature': signature,
                    "Content-Type": contentType,
                    file: file
                }
            }).then(successHandler, errorHandler, progressHandler);

Hope that helps

@rnemec
Copy link

rnemec commented May 25, 2016

@flosky, thank you for the effort putting it together (incl. the hex encoding).
One more piece to add to the solution: if somebody is using role (and thus temporary credentials), you usually get the access key and secret key from the default credentials, but these are "temporary credentials" and in order to get the signing working with the temporary ones, you have to add a tag "X-Amz-Security-Token" to both the policy.credentials array as well as in the upload.data.
The value is credentials.sessionToken (obtained on the server).
I also suggest to add the sessionToken to the object returned from "/s3_sign" so the client has the value to put into the upload.data.
Hope this helps at least one happy S3 uploader.

@redterror
Copy link

A note to any Ruby users doing this, take note that Base64.encode64 introduces newlines in the output string, and this throws off the signature. Use Base64.strict_encode64 instead.

@redterror
Copy link

redterror commented Jun 3, 2016

@rnemec - In the credential scope string, is this the target format for use with a temporary session token:

<AccessKeyId>/<SessionToken>/<YYYYMMDD>/<region>/<service>/aws4_request

The AWS docs aren't clear on exactly how to format with a session token - can you clarify?

@rnemec
Copy link

rnemec commented Jun 3, 2016

@redterror , I don't know what credential scope string is, but if you mean the string passed as 'X-Amz-Credential', the answer is NO. At least I haven't changed much @flosky 's code. To make it work with temp credentials, all I did was:

  • server: got the sessionToken from credentials object and passed into the policy:
  if (AWS.config.credentials.sessionToken) {
    policy.conditions.push(
      {'X-Amz-Security-Token': AWS.config.credentials.sessionToken}
    );
  }
  • server: also passed the sessionToken to the client in the returned JSON
  return {
    signature: signature,
    policy: base64Policy,
    date: date,
    credentials: credentials,
    expiration: expiration,
    sessionToken: AWS.config.credentials.sessionToken
  };
  • client: set the X-Amz-Security-Token in the Upload data
'X-Amz-Security-Token': response.data.sessionToken,

That's all. TLDR:

<AccessKeyId>/<YYYYMMDD>/<region>/<service>/aws4_request

@abumere
Copy link

abumere commented Jan 24, 2017

@flosky Hey I know this is old but I had a question.

  1. What extra dependencies does your server side example use?

Fairly new to all this so any help is appreciated

@flosky
Copy link

flosky commented Jan 25, 2017

To implement the functionality from above you only need your AWS credentials and the standard crypto lib that comes with node

@rootux
Copy link

rootux commented Apr 27, 2017

I'm trying to use this directive with Amazon Cognito.
Something like this:
http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/s3-example-photo-album.html
Where can I find more info on this topic?

Cognito:
http://docs.aws.amazon.com/cognito/latest/developerguide/getting-started-with-cognito-user-pools.html

@rhclayto
Copy link

rhclayto commented May 18, 2017

I'm trying to use flosky's code from above, node.js on the server & Angular upload on the front end. AWS is giving me a 500 Internal Server Error when it tries to make the upload to S3. The error message is not very informative. Does anybody see what's going on here?

<Error><Code>InternalError</Code><Message>We encountered an internal error. Please try again.</Message><RequestId>B899B43065186A2E</RequestId><HostId>5AEudnT3ALvONQk4M2uIfy5PHbFyykInWRVFozSeVW8Mpu//8S7OT9T4iHeCvzNLJzLUb10Qeg4=</HostId></Error>

Edit: I used s3.createPresignedPost to generate the POST request, sent it back to the browser, POSTed it from there, & everything worked correctly.

@sumarkan
Copy link

I need to upload the file to elastic search attachment mapper using aws signature version 4signature.
Currently does aws signature version 4 can generate the signature for external file not using s3 bucket?

@karensg karensg closed this as completed Jun 22, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests