-
Notifications
You must be signed in to change notification settings - Fork 274
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
feat(provider): imgproxy #385
base: v0
Are you sure you want to change the base?
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related to #276
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),
}
} |
resolves #378