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

S3 configuration and Node.js example #125

Open
simonexmachina opened this issue Aug 9, 2016 · 8 comments
Open

S3 configuration and Node.js example #125

simonexmachina opened this issue Aug 9, 2016 · 8 comments

Comments

@simonexmachina
Copy link

simonexmachina commented Aug 9, 2016

I think that the following info should be added to the wiki:


AWS Configuration

  • Create an access key ID for the backend using
    IAM and record the Access Key ID and Secret Access Key

  • Add a policy to allow this user to access S3

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "s3:DeleteObject",
            "s3:GetObject",
            "s3:ListBucket",
            "s3:PutObject",
            "s3:PutObjectAcl"
          ],
          "Resource": [
            "*"
          ]
        }
      ]
    }
    
  • Create a bucket on S3

  • Enable CORS

    <?xml version="1.0" encoding="UTF-8"?>
    <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
      <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Content-Type</AllowedHeader>
        <AllowedHeader>x-amz-acl</AllowedHeader>
        <AllowedHeader>origin</AllowedHeader>
        <AllowedHeader>accept</AllowedHeader>
        <ExposeHeader>Location</ExposeHeader>
      </CORSRule>
    </CORSConfiguration>
    

I've also translated the Ruby implementation into Node.js, and I think it'd be good to add this to the wiki as well:

let assert = require('assert')

let config = {
  bucket: 'your-bucket-name',
  accessKeyId: 'your-access-key',
  secretAccessKey: 'your-secret'
}

function sign(fileName, fileType, key, validForMillis = 300000) {
  assert(fileName, 'fileName query parameter is required')
  assert(fileType, 'fileName query parameter is required')
  let expires = new Date(Date.now() + validForMillis).toISOString()
  let policy = awsPolicy(expires)
  return {
    acl: 'public-read',
    AWSAccessKeyId: config.accessKeyId,
    bucket: config.fileUpload,
    expires,
    key,
    policy,
    signature: signature(policy),
    success_action_status: '201',
    'Content-Type': fileType,
    'Cache-Control': 'max-age=630720000, public'
  }
}

function signature(policy) {
  let key = config.secretAccessKey
  return crypto.createHmac('sha1', key).update(policy).digest('base64')
}

function awsPolicy(expires) {
  return new Buffer(JSON.stringify({
    expiration: expires,
    conditions: [
      { bucket: config.bucket },
      { acl: 'public-read' },
      { expires },
      { success_action_status: '201' },
      [ 'starts-with', '$key', '' ],
      [ 'starts-with', '$Content-Type', '' ],
      [ 'starts-with', '$Cache-Control', '' ],
      [ 'content-length-range', 0, 524288000 ]
    ]
  })).toString('base64')
}

// Example Express route:
app.get('/api/v1/sign-s3-upload', (req, res) => {
  let fileName = req.query.name
  let key = `uploads/${fileName}`
  res.json(sign(fileName, req.query.type, key))
})
@simonexmachina simonexmachina changed the title S3 configuration S3 configuration and Node.js example Aug 9, 2016
@digitaltoad
Copy link
Member

I will be updating the docs soon to use aws-sdk-ruby instead of hand rolling the signing process. The Node.js version could be simplified by doing the same.

@simonexmachina
Copy link
Author

Yes it looks like there’s some docs for it here
http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/node-examples.html

@karellm
Copy link

karellm commented Mar 10, 2017

@digitaltoad Is there any update on that front? Does this project work with the format returned by the aws-sdk?

{ url: 'https://s3.ca-central-1.amazonaws.com/mybucket',
  fields:
   { Key: 'image.jpg',
     bucket: 'mybucket',
     'X-Amz-Algorithm': 'AWS4-HMAC-SHA256',
     'X-Amz-Credential': 'AKIAI...',
     'X-Amz-Date': '20170310T213715Z',
     Policy: 'eyJle...',
     'X-Amz-Signature': '9c3...' 
  }
}

@digitaltoad
Copy link
Member

Right now it would not. The s3 uploader is looking for either an endpoint or a bucket name in the json response from signing.

@digitaltoad
Copy link
Member

Sorry for the lack of updates. I have been very busy and haven't been able to update this part of the project. I'm not sure when I'll be able to get to it. As a quick fix, you could look at the s3 uploader and override the upload method.

@karellm
Copy link

karellm commented Mar 10, 2017

@digitaltoad At the moment I have a hard time getting this to work at all (following instructions or not). S3 doesn't recognize the signature. As soon as I have this working I will see if I can make a PR.

Thanks for the prompt answer!

@jamesfid
Copy link

jamesfid commented Mar 21, 2017

@karellm I've had luck using the aws-sdk response directly. Your server needs to only respond with the fields key from the aws-sdk response as the signingUrl payload. From there, since ember-uploader proxies the key/values as form names, it will set everything correctly as AWS is expecting per: http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-authentication-HTTPPOST.html

In short, based on your example, your server's response to the signing request will look like:

   { Key: 'image.jpg',
     bucket: 'mybucket',
     'X-Amz-Algorithm': 'AWS4-HMAC-SHA256',
     'X-Amz-Credential': 'AKIAI...',
     'X-Amz-Date': '20170310T213715Z',
     Policy: 'eyJle...',
     'X-Amz-Signature': '9c3...' 
  }

It's worth noting that the Ruby aws-sdk returns it as x-amz-signature (lowercase) - I'm not sure if that will matter or not, but you may need to adjust that if your example is what the SDK returns for you and AWS won't take them as-is.

@karellm
Copy link

karellm commented Mar 24, 2017

@jamesfid Thanks for the feedback. I also got it working with the aws-sdk !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants