{"record":{"id":"fe8f767ad08632e6","repo":"nopSolutions/nopCommerce","slug":"hash-validation-failed","errorCode":null,"errorMessage":"Hash validation failed","messagePattern":"Hash validation failed","errorType":"exception","errorClass":"NopException","httpStatus":null,"severity":"error","filePath":"src/Plugins/Nop.Plugin.ExternalAuth.Facebook/Controllers/FacebookDataDeletionController.cs","lineNumber":85,"sourceCode":"        {\n            string signed_request = form[\"signed_request\"];\n            if (string.IsNullOrEmpty(signed_request))\n                throw new NopException(\"Request data is missing\");\n\n            var split = signed_request.Split('.');\n            var signatureRaw = DecodeUrlBase64(split[0]);\n            var dataRaw = DecodeUrlBase64(split[1]);\n            if (string.IsNullOrEmpty(signatureRaw) || string.IsNullOrEmpty(dataRaw))\n                throw new NopException(\"Part of the request data is missing\");\n\n            var signature = Convert.FromBase64String(signatureRaw);\n            var dataBuffer = Convert.FromBase64String(dataRaw);\n            var json = Encoding.UTF8.GetString(dataBuffer);\n            var appSecretBytes = Encoding.UTF8.GetBytes(_facebookExternalAuthSettings.ClientSecret);\n            HMAC hmac = new HMACSHA256(appSecretBytes);\n            var expectedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(split[1]));\n            if (!expectedHash.SequenceEqual(signature))\n                throw new NopException(\"Hash validation failed\");\n\n            var fbUser = JsonConvert.DeserializeObject<FacebookUserDTO>(json);\n            var authenticationParameters = new ExternalAuthenticationParameters\n            {\n                ProviderSystemName = FacebookAuthenticationDefaults.SystemName,\n                AccessToken = await HttpContext.GetTokenAsync(FacebookDefaults.AuthenticationScheme, \"access_token\"),\n                ExternalIdentifier = fbUser.UserId\n            };\n            var externalAuthenticationRecord = await _externalAuthenticationService.GetExternalAuthenticationRecordByExternalAuthenticationParametersAsync(authenticationParameters);\n            if (externalAuthenticationRecord is not null)\n            {\n                await _logger.InformationAsync($\"{FacebookAuthenticationDefaults.SystemName} data deletion completed. \" +\n                                               $\"CustomerId: {externalAuthenticationRecord.CustomerId}, \" +\n                                               $\"CustomerEmail: {externalAuthenticationRecord.Email}, \" +\n                                               $\"ExternalAuthenticationRecordId: {externalAuthenticationRecord.Id}\");\n\n                await _externalAuthenticationService.DeleteExternalAuthenticationRecordAsync(externalAuthenticationRecord);\n            }","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/nopSolutions/nopCommerce/blob/64bdf2ff08c8b39e65717bcf974fb43dc2ef68f2/src/Plugins/Nop.Plugin.ExternalAuth.Facebook/Controllers/FacebookDataDeletionController.cs#L67-L103","documentation":"Thrown by FacebookDataDeletionController.DataDeletionCallback as a NopException when the HMAC-SHA256 of split[1] (the payload) computed with the app secret does not match the provided signature. This is the cryptographic integrity check for Facebook signed requests.","triggerScenarios":"The signature in signed_request does not match HMAC-SHA256(payload, appSecret). Causes: the configured ClientSecret is wrong/different from the app that signed the request, the payload was tampered with, or the app has multiple secrets and the wrong one is configured.","commonSituations":"Rotated the Facebook app secret but did not update the plugin config; copied config from a different Facebook app; payload altered in transit; using the wrong environment's secret.","solutions":["Update _facebookExternalAuthSettings.ClientSecret to the current Facebook App Secret for the app that issued the callback.","After rotating a Facebook app secret, immediately update the plugin configuration for all stores/environments.","Confirm the request was not modified by a proxy (HTTPS termination, body rewriting)."],"exampleFix":"// before: ClientSecret is stale / from another app\n// after: set current secret from developers.facebook.com > App Settings > Advanced\nsettings.ClientSecret = \"<current app secret>\";\nawait _settingService.SaveSettingAsync(settings, storeScope);","handlingStrategy":"validation","validationCode":"var expected = new HMACSHA256(Encoding.UTF8.GetBytes(secret)).ComputeHash(Encoding.UTF8.GetBytes(payload));\nif (!expected.SequenceEqual(signature))\n    // secret mismatch — alert ops to rotate/update config","typeGuard":null,"tryCatchPattern":"try { return await DataDeletionCallback(form); }\ncatch (NopException ex) when (ex.Message.Contains(\"Hash validation failed\"))\n{ logger.Error(\"Facebook signature mismatch — check app secret config.\", ex); return Unauthorized(); }","preventionTips":["Keep the configured ClientSecret in sync with the Facebook app secret.","Update config immediately after rotating secrets.","Treat signature mismatch as a security event, not a transient error."],"tags":["external-auth","plugins","facebook","security","hmac","secrets","webhook"],"backgroundTag":null,"analyzedSha":"64bdf2ff08c8b39e65717bcf974fb43dc2ef68f2","analyzedAt":"2026-08-13T21:19:38.062Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}