feat(provider): imgproxy#385
Conversation
resolves nuxt#378
|
I ran yarn install with the latest version of yarn 1 but it has removed all " from the lock file. I also had to set the test src for imgproxy to a static string of |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## v0 #385 +/- ##
==========================================
+ Coverage 59.32% 60.40% +1.08%
==========================================
Files 27 28 +1
Lines 622 639 +17
Branches 196 156 -40
==========================================
+ Hits 369 386 +17
Misses 253 253 ☔ View full report in Codecov by Sentry. |
| key: 'xxxxxxxxxxxxxx', | ||
| salt: 'xxxxxxxxxxxxxx' |
There was a problem hiding this comment.
Provider options are also included in the pre-built JS that the web browser downloads to generate the image URL on the client side. Therefore, it is possible for a third party to steal the key and salt, but is it safe to leak these values?
There was a problem hiding this comment.
Provider options are also included in the pre-built JS that the web browser downloads to generate the image URL on the client side. Therefore, it is possible for a third party to steal the key and salt, but is it safe to leak these values?
@misaon This PR is implementing #378 (comment)
Do you have any insight for the above?
|
Hi! I'm pending this issue for #276. In order to being able generate hashes without exposing secret to the client we need possibilities form nuxt 3 / nitro. |
|
when will this be released :) |
|
Is this MR still block because there is no way to sign request? Is possible to see it in the new V1 module, because imgproxy is quite faster then IPX |
|
I've made my own custom provider for imgproxy to use with Nuxt 3 and Cloudflare. I haven't worried about the imgproxy keys being public, it would be nice to see how to handle the provider being server-side only and how it work at runtime. Here is the code for my custom nuxt 3 imgproxy provider. import { encodeURI } from 'js-base64'
import { Buffer } from 'buffer'
import { OperationGeneratorConfig, OperationMapper } from '~/types/image'
import { joinURL, withBase } from 'ufo'
import hmacSHA256 from 'crypto-js/hmac-sha256.js'
import Base64url from 'crypto-js/enc-base64url.js'
import hex from 'crypto-js/enc-hex.js'
const hexDecode = (hex: string) => Buffer.from(hex, 'hex')
function createMapper(map: Record<string, string>): OperationMapper {
return (key?: string) => {
return key ? map[key] ?? key : map.missingValue
}
}
function createOperationsGenerator({
formatter,
keyMap,
joinWith = '/',
valueMap,
}: OperationGeneratorConfig = {}) {
if (!formatter) {
formatter = (key, value: string) => `${key}=${value}`
}
if (keyMap && typeof keyMap !== 'function') {
keyMap = createMapper(keyMap)
}
const map = valueMap ?? {}
Object.keys(map).forEach((valueKey) => {
if (typeof map[valueKey] !== 'function') {
map[valueKey] = createMapper(map[valueKey])
}
})
return (modifiers: { [key: string]: string } = {}) => {
const operations = Object.entries(modifiers)
.filter(([_, value]) => typeof value !== 'undefined')
.map(([key, value]) => {
const mapper = map[key]
if (typeof mapper === 'function') {
value = mapper(modifiers[key])
}
key = typeof keyMap === 'function' ? keyMap(key) : key
return formatter?.(key, value)
})
return operations.join(joinWith)
}
}
const sign = (salt: string, target: string, secret: string) => {
const msg = hexDecode(salt + Buffer.from(target).toString('hex')) // Uint8Array of arbitrary length
const hmac = hmacSHA256(hex.parse(msg.toString('hex')), hex.parse(secret))
const digest = hmac.toString(Base64url)
return digest
}
const operationsGenerator = createOperationsGenerator({
keyMap: {
resize: 'rs',
size: 's',
fit: 'rt',
width: 'w',
height: 'h',
dpr: 'dpr',
enlarge: 'el',
extend: 'ex',
gravity: 'g',
crop: 'c',
padding: 'pd',
trim: 't',
rotate: 'rot',
quality: 'q',
maxBytes: 'mb',
background: 'bg',
backgroundAlpha: 'bga',
blur: 'bl',
sharpen: 'sh',
watermark: 'wm',
preset: 'pr',
cacheBuster: 'cb',
stripMetadata: 'sm',
stripColorProfile: 'scp',
autoRotate: 'ar',
filename: 'fn',
format: 'f',
},
formatter: (key, value) => `${key}:${value}`,
})
export const getImage: ProviderGetImage = (
src,
{ modifiers = {}, baseURL = '', key, salt } = {},
) => {
const encodedUrl = encodeURI(src)
const path = joinURL('/', operationsGenerator(modifiers), encodedUrl)
const signature = sign(salt, path, key)
return {
url: withBase(joinURL(signature, path), baseURL),
}
} |
|
closing as v0 is no longer supported for some time. if this is still needed, I'd welcome rebasing on |
resolves #378