imgix / react-imgix

React component to display imgix images
https://www.npmjs.com/package/react-imgix
Other
359 stars 65 forks source link

Using server-signed urls with the <Imgix /> component #580

Closed Cali93 closed 4 years ago

Cali93 commented 4 years ago

Before you submit:

Question A clear and concise description of your question

Hi, I've been trying to implement secure urls in combination with the react-imgix library. But I'm experiencing some issues.

I thought it would be as straight forward as making a request to our server that would sign the request and send back the signed src along with the src set.

However, when I pass in the secure src to the Imgix component, it appends extra params after the "s" param. Like so: s=df3493c41316c15e91210xxxxcbd72c&auto=format&w=320

Here is my component:


const Image: React.FC<{ alt: string; srcSet: string } & SharedImigixAndSourceProps> = ({
    width,
    src,
    srcSet,
    className,
    alt,
    htmlAttributes,
    ...rest
}) => {
    return (
        <Imgix
            className={cx('lazyload', className)}
            width={width}
            src={src}
            disableLibraryParam={true}
            attributeConfig={{
                src: 'data-src',
                srcSet: 'data-srcset',
                sizes: 'data-sizes',
            }}
            htmlAttributes={{
                src,
                srcSet, // I tried with and without it
                alt,
                ...htmlAttributes,
            }}
            {...rest}
        />
    );
};`
ericdeansanchez commented 4 years ago

Hey @Cali93 👋 sorry for such a delayed response.


Additions from @frederickfogerty, creator of this library.

Firstly, I understand your pain with regards to signing images with react-imgix. It's not something we actively support for the reason that we don't want people to leak their tokens to their users, and the correct way to implement it (which is how you're doing it), is difficult to do so we don't recommend it.

Having said that, I'll happily discuss this use case. The thorny part of this here is that because we require both the image url and the parameters used to be signed, react-imgix can't add any of its own parameters to the image after it has been signed on the server. Because of this, react-imgix loses its usefulness in this scenario, and so my recommendation would just be to use imgix-core-js (as @sherwinski alluded to below) with a plain img component.

My solution would be as follows:

Server-side

On the server, I assume you're signing the urls and then passing this through some API call/data layer to the frontend, which is being passed to your Image component. Thus, on the server I recommend to do this (if you're not already):

import ImgixClient from 'imgix-core-js';

const client = new ImgixClient({
    domain: '...',
    secureURLToken: '...',
});

// when you transform your images
// params will need to contain any params you want to set for this image, nothing can be changed on frontend
const signImageUrl = (imagePath: string, params: Object): { src: string, srcset: string} => {
    const params = {
        auto: "format,compress",
        ...params
    } as const
    const src = client.buildUrl(imagePath, params);
    const srcset = client.buildSrcSet(imagePath, params);
    return { src, srcset };
}

Client

Then, this data would make its way down to your frontend, and your react component would look like this:

const Image: React.FC<{ alt: string; srcSet: string }> = ({
    height,
    src,
    srcset,
    sizes,
    className,
    alt,
    ...rest
}) => (
    <img
        className={cx('lazyload', className)}
        data-src={src}
        data-srcset={srcset}
        data-sizes={sizes}
        alt={alt}
        {...rest}
    />
)

Unfortunately due to the fact that this is such a niche use case and because signing urls on the server doesn't fit well with react-imgix (as we can't change any parameters on the frontend), this is not something we will probably look at supporting natively in react-imgix - but this doesn't mean something like the above can't be done in user-land.

I hope this helps! Please come back with any more questions you might have about this.


@ericdeansanchez's response:

Securing URLs does not require you to make any requests to any server.

Officially, the way URL signing works is

To secure an image URL, we use a cryptographic hash function called "MD5" to combine the original URL with your source's unique, secure token: {YOUR TOKEN}. The result of this hash function is then appended to the end of your unsigned URL with the s parameter, creating a signed URL.

If the path or parameters of your URL are altered after the s parameter has been set, the altered URL will return a "404 Not Found" code instead of an image. This prevents unauthorized parties from changing the parameters on your URLs, but it also means that if you need to change the parameters yourself, you'll need to re-sign the new URL.

In other words, if you've supplied your imgix secure URL token to the Imgix component your URLs will be signed with your token. Here is a complete tutorial on securing/signing imgix URLs.

Hope this helps, please feel free to follow up and/or open another issue if you have any more questions.

sherwinski commented 4 years ago

Hey @Cali93, to add to that you may want to utilize the disableSrcSet prop. That way, the component will not append extra parameters when generating your srcset list. Also, it may be helpful for you to use a library such as imgix-core-js to generate a signed srcset list server-side and pass that directly into the srcset attribute on the component. Hope that helps.

frederickfogerty commented 4 years ago

Hey @Cali93 👋 Fred here - creator of this library. I added some more of my thoughts about this issue in @ericdeansanchez's response. Hope this helps!

ericdeansanchez commented 4 years ago

Thanks @frederickfogerty !!!

Cali93 commented 4 years ago

Thank you for your answers @frederickfogerty @ericdeansanchez @sherwinski. I ended up using the approach recommended by @frederickfogerty and it works like a charm. That's what I was already planning to do if the react Imgix component wasn't compatible with secure urls.

ericdeansanchez commented 4 years ago

That's awesome! Glad to hear things worked out!