Update: Authenticated Access to S3

tl;dr

Trello will begin requiring API key and token authorization via the Authorization header to access card attachment download URLs.

Update: This was previously announced but the implementation has changed enough that we are re-announcing. Query parameter-based authorization will be turned off on January 25, 2021. The manually built /download/ routes we previously recommended continue to be our recommendation moving forward.

We will be reaching out directly to developers who are using query parameters for authorization to ensure that your applications are updated before query parameter-based auth is turned off.

Timeline

Authorization for attachments will be turned on for individual enterprises on an enterprise-by-enterprise fashion. We will create a new changelog card at the point in time it is going to be turned on for all attachments.

As of right now, you can construct the future-proof /download/ URLs and pass in an Authorization header. We HIGHLY recommend updating to use this access pattern now as no changes will be required when authorization is required. More on this in Opt In To Try New Routes below.

The previously announced query-based authorization will be turned off on January 25, 2021.

Details

Currently, when you make a request to GET a file attachment on a card, you will receive back a payload that includes the URL at which the file is hosted.

For instance, with the following request:

curl https://api.trello.com/1/cards/{idCard}/attachments/?fields=url&key={{apiKey}}&token={{apiToken}}

You’d get back a HTTP 200 response with the following body:

[{
  "id": "5ef22a288dcee602857a9990",
  "url": "https://trello-attachments.s3.amazonaws.com/5b6893f01cb3228998cf629e/5b6b3ed249cf2381d501427c/c017c7020704c12468c868be104e4ed4/me.png"
}]

The URL provided in url is publicly available and requires no authorization of any sort to access.


Moving forward, public access to these files will be turned off. And the value returned for the url will no longer be the location where the file is hosted. Instead it will be a URL that includes /download/ in the path, similar to below:

[{
  "id": "5ef22a288dcee602857a9990",
  "url": "https://api.trello.com/1/cards/5edfa37673e537161016361c/attachments/5ef22a288dcee602857a9990/download/Screen_Shot_2020-06-23_at_11.13.18_AM.png"
}]

The /download/ URL format is the following:

https://api.trello.com/1/cards/{idCard}/attachments/{idAttachment}/download/{attachmentFileName}

If you are using the files directly in your application as a single user, you can add in an API key and token to the request to the /download/ URL.

Making a GET request with the key and token in the Authorization header will return the hosted file.

For instance, here is how you’d make the request with curl for an attachment with the ID 5edfd184387b678655b58348 and the attachment file named my_image.png:

curl -H "Authorization: OAuth oauth_consumer_key=\"{{key}}\", oauth_token=\"{{token}}\"" https://api.trello.com/1/cards/5e839f3696a55979a932b3ad/attachments/5edfd184387b678655b58348/download/my_image.png

If your application needs to give broader access to the file (like showing the file to multiple users), you do not want to leak the key and token. Instead, your client should download a local copy of the file and then manage access appropriately.

Opt In To Try New Routes

You can currently construct the /download/ routes and pass in authorization. We HIGHLY recommend updating to use this access pattern now as no changes will be required when authorization is required.

When constructing the new routes, remember that the name property is user modifiable and may change. For use as a file path either use the new fileName property, or parse the file name out of the url.

2 Likes

Can i access to private board/card’s images without login?
(I have generated the attachment URL with authorization header, now I want to send image url to another person). Since the user doesn’t have the login, can he access it? Or any other way to send a downloadable link to others? (My board is private).
I’m trying to download the attachment to the local and send it from there(local).
When try to download from private board, it says unauthorized permission requested
Below is code sample that I tried to download the image to local.

function file_get_contents_curl($url) {
    $ch = curl_init();
  
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_URL, $url);
  
    $data = curl_exec($ch);
    curl_close($ch);
    return $data;
}
$data = file_get_contents_curl('https://trello.com/1/cards/613045d422b04355c633730a/attachments/613045f5ce12596c264df8cd/download/161462203_2344483315686428_8238734825821897385_o.jpg');
  
$fp = 'logo-2.jpg';
file_put_contents( $fp, $data );

Do you send the key in the header? The request URL looks good to me.
Don’t know if its any different but im using: https://api.trello.com/ instead of https://trello.com/

I think based on that, you can’t turn around and share it publicly without first downloading the image and then sharing outside of Trello.

I tried doing that, i was able to download public board image in my local but not private board image.

How are you passing your key and token? It needs to be passed in the header in oauth1.0 format.

1 Like

Hi,
I’m a little stuck here, I tried the following:

curl -H "Authorization: OAuth oauth_consumer_key=\"{{baa4..........6de854}}\", oauth_token=\"{{cd58b12.........225dd}}\""  https://api.trello.com/1/cards/61a......99081/attachments/61b2.....fe4/download/test.txt

And I keep getting “invalid key” as a response, but I’m 100% confident the key is correct. What am I missing here? What am I doing wrong? Help is very much appreciated.

Thanks,

Daniel

Try chaning oauth_dev_key to oauth_consumer_key.

Oops, it was oauth_consumer_key originally. I copied the wrong one, updated my post.
Same result though.

M:\source>curl -v -H "Authorization: OAuth oauth_consumer_key=\"{{baa4....e854}}\", oauth_token=\"{{cd58b.....d341b5225dd}}\""  https://api.trello.com/1/cards/61a7....99081/attachments/61b2.....26fe4/download/test.txt

*   Trying 185.166.143.0...
* TCP_NODELAY set
* Connected to api.trello.com (185.166.143.0) port 443 (#0)
* schannel: SSL/TLS connection with api.trello.com port 443 (step 1/3)
* schannel: checking server certificate revocation
* schannel: sending initial handshake data: sending 185 bytes...
* schannel: sent initial handshake data: sent 185 bytes
* schannel: SSL/TLS connection with api.trello.com port 443 (step 2/3)
* schannel: failed to receive handshake, need more data
* schannel: SSL/TLS connection with api.trello.com port 443 (step 2/3)
* schannel: encrypted data got 2582
* schannel: encrypted data buffer: offset 2582 length 4096
* schannel: sending next handshake data: sending 93 bytes...
* schannel: SSL/TLS connection with api.trello.com port 443 (step 2/3)
* schannel: encrypted data got 258
* schannel: encrypted data buffer: offset 258 length 4096
* schannel: SSL/TLS handshake complete
* schannel: SSL/TLS connection with api.trello.com port 443 (step 3/3)
* schannel: stored credential handle in session cache
> GET /1/cards/61a73e.....9081/attachments/61b20......6fe4/download/test.txt HTTP/1.1
> Host: api.trello.com
> User-Agent: curl/7.55.1
> Accept: */*
> Authorization: OAuth oauth_consumer_key="{{baa4b.......6de854}}", oauth_token="{{cd58b1200........3d341b5225dd}}"
>
* schannel: client wants to read 102400 bytes
* schannel: encdata_buffer resized 103424
* schannel: encrypted data buffer: offset 0 length 103424
* schannel: encrypted data got 861
* schannel: encrypted data buffer: offset 861 length 103424
* schannel: decrypted data length: 832
* schannel: decrypted data added: 832
* schannel: decrypted data cached: offset 832 length 102400
* schannel: encrypted data buffer: offset 0 length 103424
* schannel: decrypted data buffer: offset 832 length 102400
* schannel: schannel_recv cleanup
* schannel: decrypted data returned 832
* schannel: decrypted data buffer: offset 0 length 102400
< HTTP/1.1 401 Unauthorized
< Date: Thu, 09 Dec 2021 17:19:01 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 11
< Server: globaledge-envoy
< Cache-Control: max-age=30, stale-while-revalidate=60, stale-if-error=600
< Vary: authorization, X-Cookie-Token
< X-Envoy-Upstream-Service-Time: 372
< Expect-Ct: report-uri="https://web-security-reports.services.atlassian.com/expect-ct-report/trello-edge", max-age=86400
< Strict-Transport-Security: max-age=63072000; preload
< X-Content-Type-Options: nosniff
< X-Xss-Protection: 1; mode=block
< Atl-Traceid: 76f41660fedf6cc1
< Report-To: {"group": "endpoint-1", "max_age": 600, "endpoints": [{"url": "https://dj9s4kmieytgz.cloudfront.net"}], "include_subdomains": true}
< Nel: {"report_to": "endpoint-1", "max_age": 600, "include_subdomains": true, "failure_fraction": 0.001}
<
invalid key* Connection #0 to host api.trello.com left intact

Hi, I’ve been using Integromat to automatically download the attachments from Trello. It was working fine until past few days it just stopped working. I tried to debug myself through Postman, managed to retrieve the attachment details:
https://api.trello.com/cards/61b17b.../attachments/61b1cb.../

{
    "id": "61b1cb...",
    "bytes": 84045,
    "date": "2021-12-09T09:25:32.087Z",
    "edgeColor": "#fcfcfc",
    "idMember": "5e9c72...",
    "isUpload": true,
    "mimeType": "image/png",
...
    "url": "`https://trello.com/1/cards/61b17b.../attachments/61b1cb.../download/Screenshot_2021-12-09_at_5.25.28_PM.png`",
    "pos": 16384,
    "fileName": "Screenshot_2021-12-09_at_5.25.28_PM.png"
}

With the same OAuth setup, I called the above url but was responded (401) “unauthorized permission requested”
Tried both api.trello.com and trello.com and still didn’t work.

Did I miss anything in the call?

1 Like

We got the same issue here , since few days ago withe the following message:
“The request signature we calculated does not match the signature you provided. Check your key and signing method.”

A post was split to a new topic: Accessing Authed Attachments