{"record":{"id":"2b2c1ba8b137bf41","repo":"bagisto/bagisto","slug":"forbidden","errorCode":null,"errorMessage":"Forbidden","messagePattern":"Forbidden","errorType":"http","errorClass":"AccessDeniedHttpException","httpStatus":403,"severity":"error","filePath":"packages/Webkul/Shop/src/Http/Controllers/API/AddressController.php","lineNumber":83,"sourceCode":"        Event::dispatch('customer.addresses.create.after', $customerAddress);\n\n        return new JsonResource([\n            'data' => new AddressResource($customerAddress),\n            'message' => trans('shop::app.customers.account.addresses.index.create-success'),\n        ]);\n    }\n\n    /**\n     * Update address for customer.\n     */\n    public function update(AddressRequest $request): JsonResource\n    {\n        $customer = auth()->guard('customer')->user();\n\n        $addressToUpdate = $this->customerAddressRepository->findOrFail($request->input('id'));\n\n        if ($addressToUpdate->customer_id !== $customer->id) {\n            abort(403);\n        }\n\n        Event::dispatch('customer.addresses.update.before');\n\n        $customerAddress = $this->customerAddressRepository->update(array_merge($request->only([\n            'company_name',\n            'first_name',\n            'last_name',\n            'vat_id',\n            'address',\n            'country',\n            'state',\n            'city',\n            'postcode',\n            'phone',\n            'default_address',\n            'email',\n        ]), [","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/bagisto/bagisto/blob/326bc45f17cd66d2835a934d21c264d3ee12d935/packages/Webkul/Shop/src/Http/Controllers/API/AddressController.php#L65-L101","documentation":"Shop API AddressController::update loads the address with findOrFail($request->input('id')) — an unknown id 404s first — then compares $addressToUpdate->customer_id with the authenticated customer's id and aborts(403) on mismatch. It is an ownership guard: the address exists, but it belongs to a different customer than the session's.","triggerScenarios":"PUT/PATCH to the shop address-update API where the id belongs to another customer: stale id kept in SPA state after an account switch, request replayed from a different session, or automated clients reusing fixture ids across accounts.","commonSituations":"Customer logs into a different account in the same browser while the SPA keeps old address ids; QA scripts mixing fixtures from two customers; addresses re-parented after a guest-to-customer merge.","solutions":["Refetch the authenticated customer's address list after login/switch and use only those ids.","Clear persisted client state (localStorage address ids) on logout in SPAs.","When merging accounts, re-map address ownership before calling update.","Use the status difference for diagnosis: 404 = id unknown, 403 = id exists but is foreign."],"exampleFix":"// before — stale id from a previous session\nput('/api/address/update', { id: 42, ... }) // 42 belongs to another customer → 403\n\n// after — take ids from the current customer's own list\nconst mine = await get('/api/customer/addresses');\nawait put('/api/address/update', { id: mine[0].id, ... });","handlingStrategy":"validation","validationCode":"$address = $this->customerAddressRepository->find($request->input('id'));\n$customerId = auth()->guard('customer')->id();\n\nif (! $address || (int) $address->customer_id !== (int) $customerId) {\n    return response()->json(['message' => 'Address not available for this customer'], $address ? 403 : 404);\n}","typeGuard":"function ownsAddress(?object $address, int|string $customerId): bool\n{\n    return $address !== null && (int) $address->customer_id === (int) $customerId;\n}","tryCatchPattern":"use Symfony\\Component\\HttpKernel\\Exception\\AccessDeniedHttpException;\n\ntry {\n    $client->put('/api/address/update', $payload);\n} catch (AccessDeniedHttpException $e) {\n    // address id belongs to another customer — refetch the session customer's own addresses\n}","preventionTips":["Treat address ids as scoped per customer; never persist them across logins.","Purge cached address ids in SPA state on logout.","Write a test that cross-updates two customers' addresses and asserts the 403."],"tags":["bagisto","shop-api","authorization","ownership","http-403"],"backgroundTag":"http-403-forbidden","analyzedSha":"326bc45f17cd66d2835a934d21c264d3ee12d935","analyzedAt":"2026-08-17T00:51:54.202Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}