Secure Delivery

You can control who and when have access to files uploaded to your account. Secure Delivery is based on Authenticated URLs. When enabled, a user will require a token to access your content.

Token authentication has an expiration time, so you can also use it to limit URL access by time. The content will become inavailable after the token expiration. To renew access, a new URL should be generated.

When to use Secure Delivery:

  • Your users upload personal, medical, and other sensitive data.
  • You want your content to be accessed by authorized users only.
  • You want users to access your content only within a specified timeframe.

Authenticated URLs

Authenticated URLs work in conjunction with custom domains. Therefore, you need set up a custom CNAME beforehand. The Authenticated URL format depends on your custom domain's CDN provider. We use Akamai as a primary CDN provider.

Here's an Authenticated URL example for Akamai:

https://cdn.yourdomain.com/{uuid}/?token=exp={timestamp}~acl={acl}~hmac={token}

Where:

  • {uuid} is a UUID of any file in your Uploadcare project or a URI (UUID + transformations + filename), which is a URL part.
  • {acl} is an optional Access Control List parameter option. It supports wildcards (*, ?). Also, it can be String (single path) or Array (multi-paths).

ACL examples to access files with a token:

  • acl=/*/ – any file in a project
  • acl=/{uuid}/ – original file with this UUID
  • acl=/{uuid}/-/resize/640x/ – modified file version

Access tokens must be generated on your backend. Start with ready-made Akamai solutions for popular languages (their GitHub):

Read full Akamai implementation guide: Auth Token 2.0 Verification and DD.

Contact us to enable the Authenticated URLs feature.

Use with File Uploader

Uploadcare allows you to implement a security-focused workflow via Authenticated URLs. That is, you will be able to control who and when have access to your content. To use the feature, you should first set the Custom CNAME.

When using Authenticated URLs, it is still okay to upload files to your project via the File Uploader UI. Showing image previews in the file uploader dialog becomes a bit more complicated, though. This section introduces the Authenticated URLs workflow and tells you about adequately handling image previews.

Authenticated URLs and Image Previews

Uploadcare File Uploader is an Upload API client. This means that every file uploaded using the file uploader dialog first goes to our upload instances, then storage, and finally CDN. The file uploader then gets that CDN URL and loads its content to render a preview. This works just fine when every CDN URL related to your Uploadcare account is public.

Once you enable the Authenticated URLs feature, the file uploader will not be able to show image previews because it’s unaware of your tokens. To handle this correctly, you will need a file uploader to proxy requests through your proxy backend.

Without using Authenticated URLs, the file uploader loads images directly from our CDN:

-> (GET) https://ucarecdn.com/{uuid}/

Enabling Authenticated URLs requires the file uploader to load images through your proxy backend:

-> (GET) https://domain.com/preview?url=https%3A%2F%2Fcdn.domain.com%2F{uuid}%2F
-> (Redirect) https://cdn.domain.com/{uuid}/?token={token}&expire={expire}

The behavior described above is provided by the two file uploader options: previewProxy and previewUrlCallback. Depending on your use case, you can implement one or another.

Option previewProxy

Implementing the previewProxy option suits the following use cases:

  • Your application uses cookie-based authorization.
  • Your proxy backend and your app are located in the same domain (otherwise cookies will not be sent).
  • You don’t need any other image-related data beside URLs.

In the case you do want to send extra data to your proxy backend, consider using the previewUrlCallback option instead. JWT tokens or image metadata are good examples of those extras.

To go with the previewProxy option, you are only required to specify a URL for your proxy backend endpoint. For instance:

UPLOADCARE_PREVIEW_PROXY = 'https://domain.com/preview?'

Your file uploader will now load image previews via the following URL:

https://domain.com/preview?url=https%3A%2F%2Fcdn.domain.com%2F{uuid}%2F

As you can see, it simply appends a query parameter holding the image preview URL to your previewProxy option, i.e.:

newPreviewUrl = previewProxy + 'url={previewUrl}'

By default, the uploader uses url as the query parameter name. However, you can also implement some different naming, e.g.:

'https://domain.com/preview?' + 'url={previewUrl}'
'https://domain.com/preview?foo=bar' + '&url={previewUrl}'
'https://domain.com/preview?foo=bar&myUrl=' + '{previewUrl}'

Option previewUrlCallback

In case you want full control over the file uploader preview URLs, go with the previewUrlCallback option. The option gets assigned a function with a signature like:

(previewUrl, fileInfo) => previewUrl

Example:

UPLOADCARE_PREVIEW_URL_CALLBACK = function(previewUrl, fileInfo) {
    const jwtToken = getJWTToken()
    return `https://domain.com/preview?` +
      `url=${encodeURIComponent(previewUrl)}&` +
      `uuid=${fileInfo.uuid}&` +
      `token=${jwtToken}`
}

When you specify the previewUrlCallback option, previewProxy will be ignored. So, it’s either one option or another, not both.

Proxy Backend

The proxy backend is an API endpoint of your application.

It should be capable of doing the following:

  • Accept escaped preview URL as GET parameter.
  • Check user authorization.
  • Generate expired access tokens for that URL.
  • Append access tokens to the URL.
  • Send redirect to that URL.

The list above covers the basics; you can further extend it if you want.

Don’t forget to check user credentials on your backend. Make sure not to authenticate anonymous user requests, unless you want to.

Proxy Backend Example

Here is how your basic proxy backend could look like for Node.js,

app.get('/preview', (req, res) => {
  const url = req.query.url
  const user = req.user

  if (!user) {
    res.status(403).send('Authorization failed')
    return
  }

  const expire = Math.round(Date.now() / 1000) + 120
  const token = generateToken(url, expire)

  const secureUrl = url + `?token=${token}&expire=${expire}`

  res.redirect(secureUrl)
})