Просмотр исходного кода

fix: use raw query string for HMAC validation to preserve URL encoding #96

Claude 5 месяцев назад
Родитель
Сommit
2a49a3d00a
1 измененных файлов с 29 добавлено и 24 удалено
  1. 29 24
      supabase/functions/oauth-shoprenter-callback/index.ts

+ 29 - 24
supabase/functions/oauth-shoprenter-callback/index.ts

@@ -8,24 +8,36 @@ const corsHeaders = {
   'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
 }
 
-// Validate HMAC signature from ShopRenter
-function validateHMAC(query: Record<string, string>, clientSecret: string): boolean {
-  const { hmac, ...params } = query
-
-  if (!hmac) {
-    console.error('[ShopRenter] HMAC missing from request')
+// Validate HMAC signature from ShopRenter using raw query string
+function validateHMACFromRawQuery(rawQueryString: string, clientSecret: string): boolean {
+  if (!clientSecret) {
+    console.error('[ShopRenter] Client secret is empty or undefined')
     return false
   }
 
-  if (!clientSecret) {
-    console.error('[ShopRenter] Client secret is empty or undefined')
+  // Parse query string into key-value pairs preserving URL encoding
+  const params = new Map<string, string>()
+  let hmacValue = ''
+
+  rawQueryString.split('&').forEach(pair => {
+    const [key, ...valueParts] = pair.split('=')
+    const value = valueParts.join('=') // Handle values that contain '='
+    if (key === 'hmac') {
+      hmacValue = value
+    } else {
+      params.set(key, value)
+    }
+  })
+
+  if (!hmacValue) {
+    console.error('[ShopRenter] HMAC missing from request')
     return false
   }
 
-  // Build sorted query string without HMAC
-  const sortedParams = Object.keys(params)
+  // Build sorted query string without HMAC (using URL-encoded values)
+  const sortedParams = Array.from(params.keys())
     .sort()
-    .map(key => `${key}=${params[key]}`)
+    .map(key => `${key}=${params.get(key)}`)
     .join('&')
 
   console.log(`[ShopRenter] HMAC validation - sorted params: ${sortedParams}`)
@@ -36,14 +48,14 @@ function validateHMAC(query: Record<string, string>, clientSecret: string): bool
     .update(sortedParams)
     .digest('hex')
 
-  console.log(`[ShopRenter] HMAC validation - received hmac: ${hmac}`)
+  console.log(`[ShopRenter] HMAC validation - received hmac: ${hmacValue}`)
   console.log(`[ShopRenter] HMAC validation - calculated hmac: ${calculatedHmac}`)
 
   // Timing-safe comparison
   try {
     const result = timingSafeEqual(
       new TextEncoder().encode(calculatedHmac),
-      new TextEncoder().encode(hmac)
+      new TextEncoder().encode(hmacValue)
     )
     console.log(`[ShopRenter] HMAC validation result: ${result}`)
     return result
@@ -158,18 +170,11 @@ serve(wrapHandler('oauth-shoprenter-callback', async (req) => {
       )
     }
 
-    // Validate HMAC
-    const queryParams: Record<string, string> = {
-      shopname,
-      code,
-      timestamp,
-      hmac
-    }
-    if (app_url) {
-      queryParams.app_url = app_url
-    }
+    // Validate HMAC using raw query string (preserves URL encoding)
+    const rawQueryString = url.search.slice(1) // Remove leading '?'
+    console.log(`[ShopRenter] Raw query string: ${rawQueryString}`)
 
-    if (!validateHMAC(queryParams, shoprenterClientSecret)) {
+    if (!validateHMACFromRawQuery(rawQueryString, shoprenterClientSecret)) {
       return new Response(
         JSON.stringify({ error: 'HMAC validation failed' }),
         { status: 403, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }