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

Getting pre-signed URL for later use (e.g. for browsers) #130

Open
Envek opened this issue Mar 26, 2021 · 5 comments
Open

Getting pre-signed URL for later use (e.g. for browsers) #130

Envek opened this issue Mar 26, 2021 · 5 comments

Comments

@Envek
Copy link

Envek commented Mar 26, 2021

One not so common but pretty handy use-case for this library is not modifying requests to be done right now, but generating pre-signed links that contains parameters and signing information using the query string. Such URL's can be passed via API to ither systems or even end-user browsers so they can access some authenticated API handles without any AWS credentials. See Signing AWS API requests / Using GET with authentication information in the Query string for reference.

I couldn't able to find ready to use javascript solution for generation of such pre-signed links with AWS signature version 4 and desperately started copy-pasting code from aws4 source to do all the low-level crypto by myself, but during that found signQuery option that almost does what I need. While this option is documented, I completely missed it when searched for ready solutions.

And here is the code that took so long to figure out. Hope it will help others to quickly accomplish similar tasks:

import { URL } from "url"
import AWS from "aws-sdk"
import aws4 from "aws4"

// Definition
export function signUrl(rawUrl: URL | string, options: any): string {
  const url = rawUrl instanceof URL ? rawUrl : new URL(rawUrl)

  const result = aws4.sign(
    {
      host: url.host,
      path: url.pathname + url.search,
      ...options,
      signQuery: true,
    },
    AWS.config.credentials,
  )

  return new URL(result.path, url).toString()
}

// Usage example
signUrl("https://some-api-gateway-custom-domain.com/path?foo=bar&baz=qux", { service: "execute-api" })

WDYT, does it make sense to create a pull request which adds such convenience function into aws4 itself?

@febg11
Copy link

febg11 commented Apr 10, 2021

I am trying to generate presigned URLs in the browser that have a start/signed time of tomorrow with a 1 hour expire. I was using aws4fetch, can this easily be applied to that library?

@Envek
Copy link
Author

Envek commented Apr 11, 2021

@febg11, according to this library source code you can provide custom signing time in X-Amz-Date header:

aws4/aws4.js

Lines 133 to 136 in a413aad

if (query['X-Amz-Date'])
this.datetime = query['X-Amz-Date']
else
query['X-Amz-Date'] = this.getDateTime()

Custom expiration time can be provided in X-Amz-Expires header:

aws4/aws4.js

Lines 130 to 131 in a413aad

if (this.service === 's3' && !query['X-Amz-Expires'])
query['X-Amz-Expires'] = 86400

But note that only S3 obeys to expiration header, and it looks like that pre-signed link expiration can't be overriden for other Amazon APIs, so links will be valid only for 5-15 minutes, depending on particular API.

@florianbepunkt
Copy link

@Envek Thank you, this is really helpful. How would you set a content type with the url?

I'm trying to implement this with a CSV file that is returned by API gateway. I can create a presigned link, but if I open it in a browser in returns base64 data. If I open the link in postman and set Accept header to text/csv I get the correct CSV data

@Envek
Copy link
Author

Envek commented Jun 2, 2021

As far as I know there is no such possibility.

You need to workaround this in your application and your API gateway. Possibly workarounds are:

  • Forcibly specify Content-Type: text/csv in response
  • Or use query parameter or path segment to indicate format (like /report.csv will respond with CSV).
  • Or add Accept header when constructing request in your frontend code.

@florianbepunkt
Copy link

@Envek Thank you. I found a workaround. Browsers normally send acomplex Accept header. API gateway uses the first mime type of the accept header that matches your specified binary types and converts the response. Since I do not use text/html in my api, it worked adding it to the binary media types.

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

3 participants