jakub-bacic / aws_auth

A low-level library for signing AWS API requests in Dart.
MIT License
3 stars 4 forks source link

presigned s3 url? #8

Open jodinathan opened 2 years ago

jodinathan commented 2 years ago

I am not expert in AWS API so sorry if this is a noob question, but would be able to create a S3 presigned URL with this lib?

jakub-bacic commented 2 years ago

I know that there are some differences between "standard signing" and signing used for S3 buckets and to be honest, I'm not sure whether it can be used for S3 out of the box.

I'll try to check this out within the next days and I'll let you know once I know the status.

jodinathan commented 2 years ago

For the moment being I am using this function from a repo that is not maintained anymore:

// presigning higher method https://github.com/nbspou/dospace/blob/63711b66aff79e6a431999aec07a1952c07e4081/lib/src/dospace_bucket.dart#L175

// signing internals https://github.com/nbspou/dospace/blob/63711b66aff79e6a431999aec07a1952c07e4081/lib/src/dospace_client.dart#L78

Dunno if this can help you figure it out

jakub-bacic commented 2 years ago

Can you try run something like this (remember to check TODO lines)?

import 'package:aws_auth/aws_auth.dart';

AWSRequest createPresignS3Request(
  AWS4Signer signer,
  Duration expires,
  String bucketName,
  String bucketPath
) {
  // create request
  final req = AWSRequest(
    'https://$bucketName.s3.${signer.region}.amazonaws.com${bucketPath}',
  );

  // presign the request
  signer.presign(req, expires: expires);

  return req;
}

void main() async {
  // TODO: provide your AWS config and region
  final AWS_ACCESS_KEY_ID = '<redacted>';
  final AWS_SECRET_ACCESS_KEY = '<redacted>';
  final AWS_REGION = 'eu-central-1';

  // create credentials object
  final credentialsProvider = AWSStaticCredentialsProvider(
    AWS_ACCESS_KEY_ID,
    AWS_SECRET_ACCESS_KEY,
    sessionToken: null,
  );
  final signer = AWS4Signer(
    credentialsProvider,
    region: AWS_REGION,
    serviceName: 's3',
  );

  final presignedReq = createPresignS3Request(
    signer,
    Duration(minutes: 5),
    // TODO: bucket name
    'my-bucket-name',
    // TODO: path with leading slash
    '/images/my-image.png',
  );
  print(presignedReq.url);
}

It actually worked out of the box on my test AWS account but I'd check if it works properly in case there are some special characters in the path.

jakub-bacic commented 2 years ago

One thing cause I didn't ask about this... Do you want to use presigning for fetching objects from non-public S3 bucket or providing a way to upload new objects? The solution above assumes the first use case.

jodinathan commented 2 years ago

providing a way to upload new objects.
Do you think it is possible?

jakub-bacic commented 2 years ago

It looks like it's just a matter of HTTP method used. If you want to create a presigned upload url, you have to use PUT instead of GET, so in the code above change:

  final req = AWSRequest(
    'https://$bucketName.s3.${signer.region}.amazonaws.com${bucketPath}',
  );

to

  final req = AWSRequest(
    'https://$bucketName.s3.${signer.region}.amazonaws.com${bucketPath}',
    method: 'PUT',
  );

With such a link, I was able to upload a file using e.g. this curl command:

curl -X PUT "<GENERATED_LINK>" -d 'example file content'

That should work for your case unless you have some special characters in the object path. I'll try to look into it next week.

jodinathan commented 2 years ago

it worked, however there a couple things:

jakub-bacic commented 2 years ago

@jodinathan thanks for the PR!

BTW, did you manage to get it working with x-amz-acl header?

jodinathan commented 2 years ago

yes.
you add it to the request in the server to generate the URL and also add an entry to the header of the client request