{"id":358,"date":"2026-10-13T05:46:33","date_gmt":"2026-10-13T05:46:33","guid":{"rendered":"https:\/\/spgtechsoft.com\/blog\/?p=358"},"modified":"2026-10-13T05:46:33","modified_gmt":"2026-10-13T05:46:33","slug":"your-react-dashboard-looks-secure-is-your-api","status":"publish","type":"post","link":"https:\/\/spgtechsoft.com\/blog\/your-react-dashboard-looks-secure-is-your-api\/","title":{"rendered":"Your React Dashboard Looks Secure. Is Your API?"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">A support employee can open customer records but isn&#8217;t supposed to delete them.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In the dashboard, the employee can browse customer records without delete control. They can still search customer records and edit the fields their role allows.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">From the employee&#8217;s side, the restriction appears to be working.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But if the API accepts a deletion request without checking the employee&#8217;s permissions, the record can still be deleted. The missing button hasn&#8217;t stopped the operation. It has only removed the usual way of triggering it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>1. Try the Request Without the Dashboard<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A developer might restrict every button correctly in React and leave the delete endpoint unprotected.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">React can hide Delete from employees who shouldn&#8217;t use it, but the request still needs to pass a server-side permission check. That only changes the page the employee sees. It doesn&#8217;t decide what the server should allow.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">An API request can be sent without clicking the button at all.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Find the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTTP\/Reference\/Methods\/DELETE\">DELETE request<\/a> in the browser&#8217;s Network tab and try it with a test account that shouldn&#8217;t have deletion rights. If the API still deletes the record, the restriction isn&#8217;t being enforced properly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The backend needs to confirm that this employee can remove that particular record. Hiding a control in React cannot replace that check.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>2. The User Has Permission. But Which Record?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There&#8217;s another detail that can be missed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Consider two sales reps working in the same CRM. Both can edit customer records, but each should only be able to change records assigned to them. Giving them the same role shouldn&#8217;t give them access to each other&#8217;s customers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">An employee changes the record ID in an API request. The new ID belongs to another salesperson&#8217;s customer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A role check alone may miss the problem. Two sales reps can have the same role without having access to the same customers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The backend needs to check the record as well as the operation. Can this person edit this particular customer?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Read access needs the same attention. Someone who can&#8217;t view a customer record shouldn&#8217;t be able to retrieve it directly through the API.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A record ID isn&#8217;t proof that someone has<a href=\"https:\/\/api-security.owasp.org\/editions\/2023\/en\/0xa1-broken-object-level-authorization\/\"> permission to access the record.<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>3. What If the Permissions Change?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">An administrator removes an employee&#8217;s access to financial reports while the employee still has the dashboard open.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The React interface may continue displaying controls based on permissions loaded earlier. That doesn&#8217;t mean the employee should retain access to the reports.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Every request needs to reflect the employee&#8217;s current access. Cached permissions can complicate this, especially when a session stays active for hours. The application needs a way to apply access changes without relying on a page refresh.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A page refresh shouldn&#8217;t be the security mechanism.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The frontend should update what it displays, too. Showing a control that will always fail can confuse users, even when the backend correctly denies the request.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>4. Test the Requests That Should Be Rejected<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A normal demonstration won&#8217;t reveal much about this problem.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Sign in as an employee with limited access. Try a restricted API operation directly. Change a customer ID to one assigned to another team. Then check whether the server rejects the request and whether the database remains unchanged.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When evaluating<a href=\"https:\/\/share.google\/tmHV3CP0SR97qosoi\"> React JS developers in India<\/a>, ask to see these negative tests, not just a working dashboard. <a href=\"https:\/\/spgtechsoft.com\/\">A React development agency in India<\/a> should be able to explain how it checks permissions for individual records and protected operations.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Delete button can stay hidden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The important test is whether the server still says no when somebody finds another way to send the request.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A support employee can open customer records but isn&#8217;t supposed to delete them. In the dashboard, the employee can browse customer records without delete&hellip;<\/p>\n","protected":false},"author":1,"featured_media":360,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[143],"tags":[212,211,208,210,209],"class_list":["post-358","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-react-development","tag-api-authorization","tag-backend-development","tag-react-js","tag-secure-web-development","tag-web-application-security"],"_links":{"self":[{"href":"https:\/\/spgtechsoft.com\/blog\/wp-json\/wp\/v2\/posts\/358","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/spgtechsoft.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/spgtechsoft.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/spgtechsoft.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/spgtechsoft.com\/blog\/wp-json\/wp\/v2\/comments?post=358"}],"version-history":[{"count":1,"href":"https:\/\/spgtechsoft.com\/blog\/wp-json\/wp\/v2\/posts\/358\/revisions"}],"predecessor-version":[{"id":359,"href":"https:\/\/spgtechsoft.com\/blog\/wp-json\/wp\/v2\/posts\/358\/revisions\/359"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/spgtechsoft.com\/blog\/wp-json\/wp\/v2\/media\/360"}],"wp:attachment":[{"href":"https:\/\/spgtechsoft.com\/blog\/wp-json\/wp\/v2\/media?parent=358"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/spgtechsoft.com\/blog\/wp-json\/wp\/v2\/categories?post=358"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/spgtechsoft.com\/blog\/wp-json\/wp\/v2\/tags?post=358"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}