<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aswani Nayak</title>
    <description>The latest articles on DEV Community by Aswani Nayak (@aswani_nayak_cdcb2fe8ee38).</description>
    <link>https://dev.to/aswani_nayak_cdcb2fe8ee38</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3937023%2Fcf9892f5-ba86-45f8-8056-5a5f47d13ea7.jpg</url>
      <title>DEV Community: Aswani Nayak</title>
      <link>https://dev.to/aswani_nayak_cdcb2fe8ee38</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aswani_nayak_cdcb2fe8ee38"/>
    <language>en</language>
    <item>
      <title>Role-Based and Permission-Based Access Control in Spring Security</title>
      <dc:creator>Aswani Nayak</dc:creator>
      <pubDate>Mon, 05 Oct 2026 19:21:38 +0000</pubDate>
      <link>https://dev.to/aswani_nayak_cdcb2fe8ee38/role-based-and-permission-based-access-control-in-spring-security-3l32</link>
      <guid>https://dev.to/aswani_nayak_cdcb2fe8ee38/role-based-and-permission-based-access-control-in-spring-security-3l32</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Once authentication is solid, the next question is inevitable: &lt;em&gt;what is this user actually allowed to do?&lt;/em&gt; Authentication proves &lt;strong&gt;who&lt;/strong&gt; you are; authorization decides &lt;strong&gt;what&lt;/strong&gt; you can access. Conflating the two—or implementing authorization carelessly—is a common source of privilege-escalation bugs.&lt;/p&gt;

&lt;p&gt;In my previous post, &lt;a href="https://dev.to/aswani_nayak_cdcb2fe8ee38/building-a-full-enterprise-ready-react-spring-boot-auth-flow-an-end-to-end-guide-pip"&gt;Building a Full Enterprise-Ready React + Spring Boot Auth Flow&lt;/a&gt;, I promised to expand on role-based access. This post delivers that: we'll cover both &lt;strong&gt;role-based access control (RBAC)&lt;/strong&gt; and the more granular &lt;strong&gt;permission-based access control&lt;/strong&gt;, how they differ, and how to implement each cleanly in Spring Security.&lt;/p&gt;

&lt;h2&gt;
  
  
  Roles vs. Permissions: What's the Difference?
&lt;/h2&gt;

&lt;p&gt;These terms are often used interchangeably, but they model access at different levels of granularity.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Granularity&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Role&lt;/td&gt;
&lt;td&gt;Coarse&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ROLE_ADMIN&lt;/code&gt;, &lt;code&gt;ROLE_USER&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Broad user categories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permission (authority)&lt;/td&gt;
&lt;td&gt;Fine&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;document:read&lt;/code&gt;, &lt;code&gt;document:delete&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Specific actions on resources&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A useful mental model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Roles&lt;/strong&gt; answer &lt;em&gt;"what kind of user is this?"&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permissions&lt;/strong&gt; answer &lt;em&gt;"what specific action can this user perform?"&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A role is typically a &lt;strong&gt;named bundle of permissions&lt;/strong&gt;. For example, &lt;code&gt;ROLE_EDITOR&lt;/code&gt; might group &lt;code&gt;article:read&lt;/code&gt;, &lt;code&gt;article:write&lt;/code&gt;, and &lt;code&gt;article:publish&lt;/code&gt;. This indirection is powerful: you can change what an editor can do without touching every user—you just adjust the role's permission set.&lt;/p&gt;

&lt;h3&gt;
  
  
  A note on Spring's naming
&lt;/h3&gt;

&lt;p&gt;In Spring Security, both roles and permissions are represented as &lt;code&gt;GrantedAuthority&lt;/code&gt; objects. The only real distinction is a convention: &lt;strong&gt;roles are authorities prefixed with &lt;code&gt;ROLE_&lt;/code&gt;&lt;/strong&gt;. When you use helpers like &lt;code&gt;hasRole("ADMIN")&lt;/code&gt;, Spring automatically prepends &lt;code&gt;ROLE_&lt;/code&gt;, so it checks for the authority &lt;code&gt;ROLE_ADMIN&lt;/code&gt;. When you use &lt;code&gt;hasAuthority("document:read")&lt;/code&gt;, no prefix is added.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modeling Roles and Permissions
&lt;/h2&gt;

&lt;p&gt;A clean domain model separates users, roles, and permissions so the relationships are explicit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Permission&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt; &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// e.g. "document:read"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Role&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt; &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// e.g. "ROLE_EDITOR"&lt;/span&gt;

    &lt;span class="nd"&gt;@ManyToMany&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;EAGER&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Permission&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;permissions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt; &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@ManyToMany&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;EAGER&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure gives you a many-to-many relationship in both directions: users can have multiple roles, and roles can share permissions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mapping to Spring Security Authorities
&lt;/h2&gt;

&lt;p&gt;Spring Security needs your user's roles and permissions expressed as &lt;code&gt;GrantedAuthority&lt;/code&gt; objects. The key step is flattening both roles &lt;em&gt;and&lt;/em&gt; their nested permissions into a single authority collection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserDetailsImpl&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;UserDetails&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;UserDetailsImpl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;GrantedAuthority&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getAuthorities&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;GrantedAuthority&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;authorities&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRoles&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Add the role itself (e.g. ROLE_EDITOR)&lt;/span&gt;
            &lt;span class="n"&gt;authorities&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SimpleGrantedAuthority&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;

            &lt;span class="c1"&gt;// Add each permission the role grants (e.g. article:write)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Permission&lt;/span&gt; &lt;span class="n"&gt;permission&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPermissions&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;authorities&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SimpleGrantedAuthority&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;permission&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;authorities&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getUsername&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUsername&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getPassword&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPassword&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Remaining UserDetails methods (account status flags) omitted for brevity&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Design note: Flattening permissions into the authority set at load time means your authorization checks later are simple string comparisons—no lazy loading or extra queries during request handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach 1: URL-Based Authorization
&lt;/h2&gt;

&lt;p&gt;The broadest form of access control is securing endpoints at the HTTP layer in your &lt;code&gt;SecurityFilterChain&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Bean&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;SecurityFilterChain&lt;/span&gt; &lt;span class="nf"&gt;filterChain&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpSecurity&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authorizeHttpRequests&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;
            &lt;span class="c1"&gt;// Role-based: only admins can reach admin endpoints&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestMatchers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/admin/**"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;hasRole&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ADMIN"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;// Permission-based: fine-grained control on a specific action&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestMatchers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpMethod&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DELETE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/api/documents/**"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasAuthority&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"document:delete"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;// Multiple roles allowed&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestMatchers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/reports/**"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;hasAnyRole&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ADMIN"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"MANAGER"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;anyRequest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;authenticated&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;URL-based rules are great for broad strokes, but they live far from your business logic and can't easily express rules like &lt;em&gt;"users can only edit their own documents."&lt;/em&gt; For that, we need method-level security.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach 2: Method-Level Authorization
&lt;/h2&gt;

&lt;p&gt;Method security lets you place authorization rules directly on service or controller methods. First, enable it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="nd"&gt;@EnableMethodSecurity&lt;/span&gt; &lt;span class="c1"&gt;// enables @PreAuthorize, @PostAuthorize, etc.&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MethodSecurityConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;@PreAuthorize&lt;/code&gt; — check before the method runs
&lt;/h3&gt;

&lt;p&gt;This is the most common and flexible annotation. It evaluates a SpEL expression before invocation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DocumentService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Role-based check&lt;/span&gt;
    &lt;span class="nd"&gt;@PreAuthorize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hasRole('ADMIN')"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;deleteAllDocuments&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Permission-based check&lt;/span&gt;
    &lt;span class="nd"&gt;@PreAuthorize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hasAuthority('document:write')"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Document&lt;/span&gt; &lt;span class="nf"&gt;createDocument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DocumentRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Combining checks&lt;/span&gt;
    &lt;span class="nd"&gt;@PreAuthorize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hasRole('MANAGER') and hasAuthority('report:export')"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;exportReport&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;reportId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using method arguments in expressions
&lt;/h3&gt;

&lt;p&gt;The real power of &lt;code&gt;@PreAuthorize&lt;/code&gt; is referencing method parameters, enabling ownership-style rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Allow if the user has the permission AND owns the resource&lt;/span&gt;
&lt;span class="nd"&gt;@PreAuthorize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hasAuthority('document:edit') and #document.ownerId == authentication.principal.id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Document&lt;/span&gt; &lt;span class="nf"&gt;updateDocument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Document&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;@PostAuthorize&lt;/code&gt; — check after the method runs
&lt;/h3&gt;

&lt;p&gt;Sometimes you can only decide once you've seen the result—for example, checking ownership of a fetched entity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PostAuthorize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"returnObject.ownerId == authentication.principal.id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Document&lt;/span&gt; &lt;span class="nf"&gt;getDocument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;documentRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caution: &lt;code&gt;@PostAuthorize&lt;/code&gt; runs the method first and only then blocks the return value. Never use it for operations with side effects (like deletes)—the work is already done by the time authorization fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach 3: Custom Permission Logic
&lt;/h2&gt;

&lt;p&gt;When expressions get complex or repetitive, extract the logic into a reusable bean and call it from SpEL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"documentSecurity"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DocumentSecurity&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;canEdit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;documentId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Authentication&lt;/span&gt; &lt;span class="n"&gt;authentication&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Custom logic: ownership, shared access, org membership, etc.&lt;/span&gt;
        &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nc"&gt;UserDetailsImpl&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;authentication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPrincipal&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;documentRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isOwnerOrCollaborator&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;documentId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reference it in an annotation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PreAuthorize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@documentSecurity.canEdit(#id, authentication)"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Document&lt;/span&gt; &lt;span class="nf"&gt;updateDocument&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DocumentRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps complex rules testable, reusable, and out of your annotations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting to the Frontend
&lt;/h2&gt;

&lt;p&gt;Authorization must be enforced on the &lt;strong&gt;backend&lt;/strong&gt;—the frontend check is purely for UX. Never rely on hiding a button as your security boundary; a determined user can still call the API directly.&lt;/p&gt;

&lt;p&gt;That said, exposing the user's permissions to React lets you tailor the UI. Return authorities from an endpoint like &lt;code&gt;/auth/me&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jane"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"roles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ROLE_EDITOR"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"article:read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"article:write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"article:publish"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then gate UI elements with a small helper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useAuth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./AuthContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;usePermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;permission&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAuth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;permission&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage in a component&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;PublishButton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canPublish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;article:publish&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;canPublish&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Publish&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reminder: This only improves the experience. The backend &lt;code&gt;@PreAuthorize&lt;/code&gt; check is what actually protects the action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Pitfalls to Avoid
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pitfall&lt;/th&gt;
&lt;th&gt;Consequence&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Trusting frontend checks only&lt;/td&gt;
&lt;td&gt;Trivial privilege escalation&lt;/td&gt;
&lt;td&gt;Always enforce on the backend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forgetting the &lt;code&gt;ROLE_&lt;/code&gt; prefix&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;hasRole&lt;/code&gt; checks silently fail&lt;/td&gt;
&lt;td&gt;Let Spring add the prefix; store authorities consistently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Using &lt;code&gt;@PostAuthorize&lt;/code&gt; on side-effecting methods&lt;/td&gt;
&lt;td&gt;Action runs before the check&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;@PreAuthorize&lt;/code&gt; for mutations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hardcoding roles in many places&lt;/td&gt;
&lt;td&gt;Brittle, hard to change&lt;/td&gt;
&lt;td&gt;Model roles as bundles of permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overly coarse roles&lt;/td&gt;
&lt;td&gt;Can't express fine-grained rules&lt;/td&gt;
&lt;td&gt;Add permission-level authorities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Not enabling method security&lt;/td&gt;
&lt;td&gt;Annotations silently ignored&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;@EnableMethodSecurity&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Bringing It Together
&lt;/h2&gt;

&lt;p&gt;A robust authorization layer uses the right tool at each level:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;URL-based rules&lt;/strong&gt; for broad, endpoint-level gating.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Method-level &lt;code&gt;@PreAuthorize&lt;/code&gt;&lt;/strong&gt; for business-rule and ownership checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom security beans&lt;/strong&gt; for complex, reusable logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Roles as bundles of permissions&lt;/strong&gt; for maintainable, flexible access models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend checks for UX only&lt;/strong&gt;, with the backend as the true boundary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Roles give you convenient, coarse categories; permissions give you precise control. Most real applications benefit from using both together.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Note on Verification
&lt;/h2&gt;

&lt;p&gt;The code samples here are illustrative and follow common, modern Spring Security patterns. Because these APIs evolve across versions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm the method-security annotation setup for your version. &lt;code&gt;@EnableMethodSecurity&lt;/code&gt; is the current approach, but older code may use &lt;code&gt;@EnableGlobalMethodSecurity&lt;/code&gt; with different attributes.&lt;/li&gt;
&lt;li&gt;Verify SpEL expression support and the exact &lt;code&gt;principal&lt;/code&gt; type available in your &lt;code&gt;authentication&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;Test both the allow and deny paths for every protected method—authorization bugs often hide in the cases you didn't test.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  If you'd like, I can expand this into a focused follow-up on &lt;strong&gt;testing authorization rules&lt;/strong&gt; or &lt;strong&gt;integrating an external identity provider (like Keycloak) for centralized RBAC&lt;/strong&gt;. Let me know.
&lt;/h2&gt;

</description>
      <category>springboot</category>
      <category>security</category>
      <category>permission</category>
      <category>authorization</category>
    </item>
    <item>
      <title>Building a Full Enterprise-Ready React + Spring Boot Auth Flow: An End-to-End Guide</title>
      <dc:creator>Aswani Nayak</dc:creator>
      <pubDate>Fri, 21 Aug 2026 20:46:06 +0000</pubDate>
      <link>https://dev.to/aswani_nayak_cdcb2fe8ee38/building-a-full-enterprise-ready-react-spring-boot-auth-flow-an-end-to-end-guide-pip</link>
      <guid>https://dev.to/aswani_nayak_cdcb2fe8ee38/building-a-full-enterprise-ready-react-spring-boot-auth-flow-an-end-to-end-guide-pip</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Authentication is one of those things that looks simple in a tutorial and becomes surprisingly complex in production. Between token storage, CSRF protection, refresh flows, and protected routing, there are many places to get it wrong—and getting it wrong has real security consequences.&lt;/p&gt;

&lt;p&gt;In two earlier posts, I covered pieces of this puzzle: &lt;a href="https://dev.to/aswani_nayak_cdcb2fe8ee38/enabling-csrf-in-a-jwt-based-react-spring-boot-application-enterprise-ready-approach-8a0"&gt;Enabling CSRF in a JWT-Based React + Spring Boot Application&lt;/a&gt; and &lt;a href="https://dev.to/aswani_nayak_cdcb2fe8ee38/storing-personal-information-in-react-sessionstorage-vs-context-api-25pa"&gt;Storing Personal Information in React: sessionStorage vs Context API&lt;/a&gt;. This post ties those threads together into a complete, end-to-end authentication flow you can adapt for enterprise applications.&lt;/p&gt;

&lt;p&gt;We'll walk through the full journey: login → token issuance → secure storage → protected routes → token refresh → logout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;Before the code, here's the high-level flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────┐         ┌──────────────────┐
│    React     │         │   Spring Boot    │
│   Frontend   │         │     Backend      │
└──────┬───────┘         └────────┬─────────┘
       │  1. POST /login          │
       │─────────────────────────&amp;gt;│
       │                          │  validate credentials
       │  2. JWT (httpOnly cookie)│  issue access + refresh
       │&amp;lt;─────────────────────────│
       │                          │
       │  3. GET /protected       │
       │  (+ CSRF token)          │
       │─────────────────────────&amp;gt;│  validate JWT + CSRF
       │  4. Protected data       │
       │&amp;lt;─────────────────────────│
       │                          │
       │  5. POST /refresh        │
       │─────────────────────────&amp;gt;│  rotate tokens
       │                          │
       │  6. POST /logout         │
       │─────────────────────────&amp;gt;│  invalidate session
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Design Decisions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decision&lt;/th&gt;
&lt;th&gt;Choice&lt;/th&gt;
&lt;th&gt;Rationale&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Token storage&lt;/td&gt;
&lt;td&gt;httpOnly cookies&lt;/td&gt;
&lt;td&gt;Not accessible to JavaScript → mitigates XSS token theft&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSRF protection&lt;/td&gt;
&lt;td&gt;Double-submit / token pattern&lt;/td&gt;
&lt;td&gt;Required when using cookies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token type&lt;/td&gt;
&lt;td&gt;Short-lived access + refresh&lt;/td&gt;
&lt;td&gt;Limits exposure window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State management&lt;/td&gt;
&lt;td&gt;Context API for auth status&lt;/td&gt;
&lt;td&gt;Centralized, lightweight&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Why httpOnly cookies over localStorage? As I discussed in the storage blog, localStorage is readable by any script on the page—making it vulnerable to XSS. httpOnly cookies trade that risk for the need to handle CSRF, which we address below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Backend — Login and Token Issuance
&lt;/h2&gt;

&lt;p&gt;On successful authentication, the backend issues a JWT and sets it as an httpOnly, secure cookie rather than returning it in the response body.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/login"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;LoginRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                               &lt;span class="nc"&gt;HttpServletResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Authenticate credentials (delegated to AuthenticationManager)&lt;/span&gt;
    &lt;span class="nc"&gt;Authentication&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;authenticationManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authenticate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UsernamePasswordAuthenticationToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUsername&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPassword&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;

    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jwtService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generateAccessToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;refreshToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jwtService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generateRefreshToken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Set access token as httpOnly cookie&lt;/span&gt;
    &lt;span class="nc"&gt;ResponseCookie&lt;/span&gt; &lt;span class="n"&gt;accessCookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ResponseCookie&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"access_token"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;httpOnly&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sameSite&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Strict"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maxAge&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofMinutes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addHeader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpHeaders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SET_COOKIE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accessCookie&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="c1"&gt;// Refresh token typically set on a scoped path, e.g. /api/auth/refresh&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LoginResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Login successful"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;httpOnly(true) prevents JavaScript access.&lt;/li&gt;
&lt;li&gt;secure(true) ensures the cookie is only sent over HTTPS.&lt;/li&gt;
&lt;li&gt;sameSite("Strict") adds a layer of CSRF defense (though we won't rely on it alone).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: Backend — CSRF Protection
&lt;/h2&gt;

&lt;p&gt;Because we're using cookies, we need CSRF protection. Spring Security supports the double-submit cookie pattern via CookieCsrfTokenRepository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Bean&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;SecurityFilterChain&lt;/span&gt; &lt;span class="nf"&gt;filterChain&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpSecurity&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;csrf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csrf&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;csrf&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;csrfTokenRepository&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CookieCsrfTokenRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withHttpOnlyFalse&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;csrfTokenRequestHandler&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SpaCsrfTokenRequestHandler&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authorizeHttpRequests&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestMatchers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/auth/login"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/api/auth/refresh"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;permitAll&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;anyRequest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;authenticated&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addFilterBefore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jwtAuthFilter&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;UsernamePasswordAuthenticationFilter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CSRF token cookie is intentionally not httpOnly, because the frontend must read it and echo it back in a request header. The attacker's site can't read your cookies cross-origin, so this remains safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Frontend — Configuring the HTTP Client
&lt;/h2&gt;

&lt;p&gt;Configure your HTTP client to send cookies and include the CSRF token on state-changing requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;withCredentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// send cookies with every request&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Attach CSRF token from cookie to outgoing requests&lt;/span&gt;
&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interceptors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;csrfToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;XSRF-TOKEN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;csrfToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-XSRF-TOKEN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;csrfToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`(^| )&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;=([^;]+)`&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;decodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Frontend — Auth State with Context API
&lt;/h2&gt;

&lt;p&gt;Since the JWT lives in an httpOnly cookie (invisible to JS), we track authentication status—not the token itself—in React Context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AuthContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AuthProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// On mount, check if an existing session is valid&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/auth/me&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/auth/login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/auth/me&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/auth/logout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AuthContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logout&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/AuthContext.Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useAuth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AuthContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Design note: We don't store the token in Context or state—only the user's authenticated status. The browser handles the cookie automatically. This is the safest pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Frontend — Protecting Routes
&lt;/h2&gt;

&lt;p&gt;Wrap protected routes so unauthenticated users are redirected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Navigate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useAuth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./AuthContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProtectedRoute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAuth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Navigate&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;replace&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt;
  &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dashboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProtectedRoute&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Dashboard&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ProtectedRoute&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Handling Token Refresh
&lt;/h2&gt;

&lt;p&gt;Short-lived access tokens improve security but require a refresh mechanism. A response interceptor can transparently retry after refreshing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interceptors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;original&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;original&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_retry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;original&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_retry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/auth/refresh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// rotates cookies server-side&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;original&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;            &lt;span class="c1"&gt;// retry original request&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;refreshError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Refresh failed → force logout&lt;/span&gt;
        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;refreshError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Security tip: Implement refresh token rotation on the backend—issue a new refresh token on each use and invalidate the old one. This limits damage if a refresh token is ever compromised.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Logout
&lt;/h2&gt;

&lt;p&gt;Logout must clear cookies server-side, since the frontend can't touch httpOnly cookies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/logout"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpServletResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;ResponseCookie&lt;/span&gt; &lt;span class="n"&gt;clearedCookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ResponseCookie&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"access_token"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;httpOnly&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maxAge&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// expire immediately&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addHeader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpHeaders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SET_COOKIE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clearedCookie&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="c1"&gt;// Also invalidate the refresh token in your store&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LogoutResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Logged out"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common Pitfalls to Avoid
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pitfall&lt;/th&gt;
&lt;th&gt;Consequence&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CORS misconfiguration&lt;/td&gt;
&lt;td&gt;Requests blocked or insecure&lt;/td&gt;
&lt;td&gt;Configure allowed origins explicitly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forgetting withCredentials&lt;/td&gt;
&lt;td&gt;Cookies not sent&lt;/td&gt;
&lt;td&gt;Set on HTTP client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-lived access tokens&lt;/td&gt;
&lt;td&gt;Large exposure window&lt;/td&gt;
&lt;td&gt;Short access + refresh tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Not rotating refresh tokens&lt;/td&gt;
&lt;td&gt;Replay attacks&lt;/td&gt;
&lt;td&gt;Rotate on every refresh&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skipping CSRF with cookie auth&lt;/td&gt;
&lt;td&gt;CSRF vulnerability&lt;/td&gt;
&lt;td&gt;Enable CSRF protection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storing JWT in localStorage&lt;/td&gt;
&lt;td&gt;XSS token theft&lt;/td&gt;
&lt;td&gt;Use httpOnly cookies&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Bringing It Together
&lt;/h2&gt;

&lt;p&gt;A production-grade auth flow isn't a single feature—it's a coordinated system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Login issues tokens as secure, httpOnly cookies.&lt;/li&gt;
&lt;li&gt;CSRF protection guards cookie-based requests.&lt;/li&gt;
&lt;li&gt;Context API tracks authentication status (never the token).&lt;/li&gt;
&lt;li&gt;Protected routes gate access on the frontend.&lt;/li&gt;
&lt;li&gt;Token refresh keeps sessions alive without long-lived tokens.&lt;/li&gt;
&lt;li&gt;Logout clears state on both ends.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each piece reinforces the others. Skip one, and you introduce a gap.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Note on Verification
&lt;/h2&gt;

&lt;p&gt;The code samples here are illustrative and follow common, widely-recommended patterns. Spring Security and React library APIs evolve, so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify the exact API for your versions (e.g., Spring Security's CSRF configuration has changed across major releases).&lt;/li&gt;
&lt;li&gt;Adapt cookie attributes (SameSite, domain, path) to your deployment topology.&lt;/li&gt;
&lt;li&gt;Always test the full flow against your own security requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post completes the authentication series that began with the &lt;a href="https://dev.to/aswani_nayak_cdcb2fe8ee38/enabling-csrf-in-a-jwt-based-react-spring-boot-application-enterprise-ready-approach-8a0"&gt;CSRF&lt;/a&gt; and &lt;a href="https://dev.to/aswani_nayak_cdcb2fe8ee38/storing-personal-information-in-react-sessionstorage-vs-context-api-25pa"&gt;storage&lt;/a&gt; articles. If there's a specific piece you'd like me to expand—refresh token rotation, CORS configuration, or role-based access—let me know.&lt;/p&gt;

</description>
      <category>react</category>
      <category>springboot</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Securing AI-Assisted Development Workflows: Best Practices for Enterprise Teams</title>
      <dc:creator>Aswani Nayak</dc:creator>
      <pubDate>Fri, 21 Aug 2026 18:54:12 +0000</pubDate>
      <link>https://dev.to/aswani_nayak_cdcb2fe8ee38/securing-ai-assisted-development-workflows-best-practices-for-enterprise-teams-5d3</link>
      <guid>https://dev.to/aswani_nayak_cdcb2fe8ee38/securing-ai-assisted-development-workflows-best-practices-for-enterprise-teams-5d3</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;br&gt;
AI coding assistants have moved from novelty to necessity. Tools that suggest, complete, and even generate entire functions are now embedded in the daily workflow of many development teams. In my earlier post, &lt;a href="https://dev.to/aswani_nayak_cdcb2fe8ee38/from-prompting-to-production-a-practical-framework-for-ai-assisted-software-development-37pp"&gt;From Prompting to Production&lt;/a&gt;, I explored how to systematically integrate these tools into software delivery. But adoption without governance creates a new attack surface.&lt;/p&gt;

&lt;p&gt;The uncomfortable truth is this: &lt;strong&gt;the same tools that accelerate your team can quietly leak your secrets, introduce vulnerabilities, and expand your compliance risk&lt;/strong&gt;. For enterprise teams—where security, privacy, and auditability are non-negotiable—AI assistance demands the same rigor we apply to any other part of the SDLC.&lt;/p&gt;

&lt;p&gt;This post lays out a practical framework for securing AI-assisted development, organized around four pillars: &lt;strong&gt;data protection, code validation, secret management, and team governance.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why This Matters&lt;/strong&gt;&lt;br&gt;
Before diving into practices, it's worth being explicit about the risks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fomamds7jf6msz9a9varv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fomamds7jf6msz9a9varv.png" alt=" " width="746" height="109"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each of these is manageable—but only with intentional guardrails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pillar 1: Protect Sensitive Data and Source Code&lt;/strong&gt;&lt;br&gt;
The most immediate concern is what leaves your environment when developers use AI tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended Practices&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Understand the data flow.&lt;/strong&gt; Know whether your AI tool sends code to external servers, and whether that data is used for training. Review the vendor's data retention and privacy terms carefully.&lt;br&gt;
&lt;strong&gt;Prefer enterprise or self-hosted deployments&lt;/strong&gt;. Where available, enterprise tiers often offer contractual guarantees that your data won't be used for model training and may support private deployments.&lt;br&gt;
&lt;strong&gt;Classify your codebase.&lt;/strong&gt; Not all repositories carry the same sensitivity. Establish clear rules about which projects may use AI assistance and which must not.&lt;br&gt;
&lt;strong&gt;Use IDE-level controls.&lt;/strong&gt; Many tools allow you to exclude specific files, directories, or file patterns (e.g., .env, config files) from being sent as context.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Vendor capabilities vary and change frequently. Always verify current data-handling policies directly with your tool provider before relying on them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Pillar 2: Validate AI-Generated Code&lt;/strong&gt;&lt;br&gt;
AI suggestions are plausible—not guaranteed correct or secure. Treat all generated code as untrusted until reviewed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Practical Review Checklist&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Does it do what you intended?&lt;/strong&gt; Read and understand every line before accepting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does it introduce known vulnerability patterns?&lt;/strong&gt; Watch for injection risks, weak cryptography, improper input validation, and insecure defaults.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does it follow your standards?&lt;/strong&gt; Naming, error handling, logging, and architectural conventions still apply.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is it tested?&lt;/strong&gt; AI-generated code deserves the same test coverage as human-written code.
&lt;strong&gt;Automate What You Can&lt;/strong&gt;
Integrate security scanning into your pipeline so that AI-generated code is subject to the same gates as everything else:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Example CI stage (illustrative)&lt;/span&gt;
&lt;span class="na"&gt;security-checks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;verify&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;run-static-analysis&lt;/span&gt;     &lt;span class="c1"&gt;# SAST tooling&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;run-dependency-scan&lt;/span&gt;     &lt;span class="c1"&gt;# detect vulnerable dependencies&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;run-secret-scan&lt;/span&gt;         &lt;span class="c1"&gt;# catch committed secrets&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$CI_PIPELINE_SOURCE'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Static analysis (SAST), dependency scanning, and secret detection are your safety net when a risky suggestion slips through review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pillar 3: Keep Secrets Out of Prompts and Suggestions&lt;/strong&gt;&lt;br&gt;
Secrets are especially dangerous in an AI context because they can be both &lt;strong&gt;leaked outward&lt;/strong&gt; (pasted into a prompt) and &lt;strong&gt;suggested inward&lt;/strong&gt; (autocompleted from patterns).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended Practices&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Never paste real credentials into prompts.&lt;/strong&gt; Use placeholders when asking for help with authentication or configuration code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use secret managers.&lt;/strong&gt; Keep credentials in a dedicated vault or secrets manager rather than in code that AI tools can read as context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enable pre-commit secret scanning.&lt;/strong&gt; Catch accidental commits before they reach your repository.&lt;br&gt;
Here's an example of writing auth code the right way—with no hardcoded secrets:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Good: secrets loaded from environment / secret manager&lt;/span&gt;
&lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${jwt.secret}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;jwtSecret&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Avoid: hardcoded secrets that AI tools may learn or expose&lt;/span&gt;
&lt;span class="c1"&gt;// private String jwtSecret = "super-secret-value";&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ties directly to enterprise auth patterns—like the JWT and CSRF approach I covered previously. Secure code and secure workflows reinforce each other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pillar 4: Establish Team Governance&lt;/strong&gt;&lt;br&gt;
Individual discipline isn't enough at enterprise scale. You need shared, documented rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building an AI Usage Policy&lt;/strong&gt;&lt;br&gt;
Consider addressing these areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Approved tools.&lt;/strong&gt; Maintain a list of vetted AI assistants and their permitted use contexts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prohibited data.&lt;/strong&gt; Explicitly define what must never be shared with AI tools (customer data, secrets, regulated information).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Review requirements.&lt;/strong&gt; Mandate human review and sign-off for AI-generated code, especially in security-sensitive areas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Training&lt;/strong&gt;. Ensure developers understand both the capabilities and the risks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Auditability&lt;/strong&gt;. Where possible, keep a record of how and where AI assistance is used in your codebase.&lt;br&gt;
&lt;strong&gt;A Simple Adoption Maturity Model&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffb9sy3asote59hkwah5x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffb9sy3asote59hkwah5x.png" alt=" " width="529" height="91"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most enterprise teams should aim for at least &lt;strong&gt;Level 3&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bringing It Together&lt;/strong&gt;&lt;br&gt;
Securing AI-assisted development isn't about slowing your team down—it's about making acceleration sustainable. The framework in short:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Protect data&lt;/strong&gt; — control what leaves your environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate code&lt;/strong&gt; — trust nothing until reviewed and scanned.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guard secrets&lt;/strong&gt; — keep credentials out of prompts and suggestions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Govern usage&lt;/strong&gt; — codify shared rules and enforce them.
AI assistance is here to stay. The teams that win won't be the ones who adopt fastest—they'll be the ones who adopt safely.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What's Next?&lt;/strong&gt;&lt;br&gt;
In a future post, I'll dive deeper into &lt;strong&gt;integrating security scanning into CI/CD pipelines&lt;/strong&gt;, expanding on Pillar 2 with concrete tooling patterns. If you found this useful, that's the natural next step in the series.&lt;/p&gt;

&lt;p&gt;Have thoughts or experiences with securing AI workflows on your team? I'd love to hear them.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>governance</category>
      <category>security</category>
      <category>websecurity</category>
    </item>
    <item>
      <title>From Prompting to Production: A Practical Framework for AI-Assisted Software Development</title>
      <dc:creator>Aswani Nayak</dc:creator>
      <pubDate>Wed, 22 Jul 2026 16:06:22 +0000</pubDate>
      <link>https://dev.to/aswani_nayak_cdcb2fe8ee38/from-prompting-to-production-a-practical-framework-for-ai-assisted-software-development-37pp</link>
      <guid>https://dev.to/aswani_nayak_cdcb2fe8ee38/from-prompting-to-production-a-practical-framework-for-ai-assisted-software-development-37pp</guid>
      <description>&lt;p&gt;&lt;em&gt;How structured workflows, context engineering, and human oversight can transform AI from a coding assistant into a true engineering partner.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Generative AI is rapidly changing how software is built. While many development teams have experimented with AI-powered coding tools, the real challenge is not generating code—it's creating a repeatable process that produces reliable, high-quality outcomes.&lt;br&gt;
Recently, we conducted a hands-on workshop to explore how AI can support the complete software development lifecycle. Rather than using toy examples, the session focused on solving a real business problem while applying a structured AI-assisted development workflow.&lt;br&gt;
The result was more than just working software. We uncovered a practical framework that helps engineers collaborate effectively with AI agents, improve productivity, and maintain engineering quality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Importance of Preparation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the key messages before the workshop was simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't start with code. Start with understanding.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Developers were encouraged to review business requirements in advance and become familiar with the problem domain before engaging coding agents.&lt;br&gt;
The goal was not simply to generate code faster, but to:&lt;br&gt;
    • Understand the business problem&lt;br&gt;
    • Validate AI-generated outputs&lt;br&gt;
    • Review implementation plans critically&lt;br&gt;
    • Verify test cases before implementation&lt;br&gt;
The workshop reinforced a crucial principle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI can accelerate development, but humans remain responsible for understanding the business intent.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Shift from Prompt Engineering to Context Engineering&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many discussions about AI focus on writing the perfect prompt.&lt;br&gt;
The workshop highlighted something more important:&lt;br&gt;
Context engineering.&lt;br&gt;
The best outcomes occurred when engineers supplied:&lt;br&gt;
    • Business objectives&lt;br&gt;
    • Functional requirements&lt;br&gt;
    • Acceptance criteria&lt;br&gt;
    • System context&lt;br&gt;
    • Integration dependencies&lt;br&gt;
    • Architectural constraints&lt;br&gt;
    • Source-of-truth considerations&lt;br&gt;
Instead of immediately asking an AI agent to generate code, the team first provided comprehensive context and required the agent to fully understand the problem.&lt;br&gt;
For example:&lt;/p&gt;

&lt;p&gt;"I want to create a new Spring Boot application for a sales platform. Review the requirements, understand the acceptance criteria, and grill me on the requirements before creating an implementation plan."&lt;/p&gt;

&lt;p&gt;The business requirement explained that sellers needed a streamlined way to serve customers while relying on existing systems as the source of truth and avoiding unnecessary data duplication across platforms.&lt;br&gt;
Providing this level of context dramatically improved the quality of AI-generated plans, test cases, and implementation recommendations.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;"Grill Me"&lt;/strong&gt; Approach&lt;/p&gt;

&lt;p&gt;One of the most valuable concepts introduced during the workshop was the Grill Me technique.&lt;br&gt;
Rather than immediately proposing a solution, the AI agent is instructed to challenge assumptions and ask follow-up questions until it understands the requirements thoroughly.&lt;br&gt;
This transforms the agent from a code generator into a business analyst.&lt;br&gt;
Instead of guessing, the AI asks questions such as:&lt;br&gt;
    • What business outcome are we trying to achieve?&lt;br&gt;
    • What systems currently own the data?&lt;br&gt;
    • What are the integration constraints?&lt;br&gt;
    • What are the edge cases?&lt;br&gt;
    • What constitutes success?&lt;br&gt;
The objective is to eliminate ambiguity before planning or implementation begins.&lt;br&gt;
This often uncovers gaps in requirements long before development starts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Structured AI Development Workflow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The workshop followed a repeatable workflow designed to maximize AI effectiveness while preserving engineering discipline.&lt;/p&gt;

&lt;p&gt;Step 1: Requirement Discovery&lt;/p&gt;

&lt;p&gt;Provide the business requirements to the AI agent and ask it to fully understand the request before proposing a solution.&lt;br&gt;
The agent should review the available context and ask clarifying questions until requirements are well understood.&lt;br&gt;
Outcome: Clear understanding of the problem.&lt;/p&gt;

&lt;p&gt;Step 2: Implementation Planning&lt;/p&gt;

&lt;p&gt;Once requirements are understood, the AI generates:&lt;br&gt;
    • Solution architecture&lt;br&gt;
    • Technical approach&lt;br&gt;
    • Task breakdown&lt;br&gt;
    • Risks and assumptions&lt;br&gt;
Outcome: An actionable implementation plan.&lt;/p&gt;

&lt;p&gt;Step 3: Human Review&lt;/p&gt;

&lt;p&gt;Developers review the proposed plan and challenge assumptions.&lt;br&gt;
This is one of the most important phases in the process.&lt;br&gt;
The objective is not to approve the plan blindly but to verify:&lt;br&gt;
    • Architectural decisions&lt;br&gt;
    • Business alignment&lt;br&gt;
    • Missing requirements&lt;br&gt;
    • Technical feasibility&lt;br&gt;
Outcome: A validated implementation strategy.&lt;/p&gt;

&lt;p&gt;Step 4: Test-Driven Development&lt;/p&gt;

&lt;p&gt;Before writing code, the AI generates test cases.&lt;br&gt;
A structured TDD approach encourages engineers to define expected system behavior before implementation begins.&lt;br&gt;
Benefits include:&lt;br&gt;
    • Better requirement validation&lt;br&gt;
    • Improved quality&lt;br&gt;
    • Higher confidence in outcomes&lt;br&gt;
    • Earlier detection of requirement gaps&lt;br&gt;
Outcome: Comprehensive test coverage aligned to business needs.&lt;/p&gt;

&lt;p&gt;Step 5: Review Test Cases&lt;/p&gt;

&lt;p&gt;Developers review generated tests to ensure:&lt;br&gt;
    • Requirements are fully represented&lt;br&gt;
    • Edge cases are covered&lt;br&gt;
    • Acceptance criteria are addressed&lt;br&gt;
Strong test cases frequently reveal requirement gaps before implementation starts.&lt;br&gt;
Outcome: Reliable validation criteria.&lt;/p&gt;

&lt;p&gt;Step 6: Code Implementation&lt;/p&gt;

&lt;p&gt;After requirements, plans, and tests have been validated, the AI generates implementation code.&lt;br&gt;
Because the earlier phases established strong guardrails, implementation quality improves significantly.&lt;br&gt;
Outcome: Faster and more accurate development.&lt;/p&gt;

&lt;p&gt;Step 7: Validation and Refinement&lt;/p&gt;

&lt;p&gt;AI-generated code should never be considered production-ready by default.&lt;br&gt;
Engineers still perform:&lt;br&gt;
    • Code reviews&lt;br&gt;
    • Functional validation&lt;br&gt;
    • Performance verification&lt;br&gt;
    • Security checks&lt;br&gt;
    • Refinement and optimization&lt;br&gt;
Outcome: Production-quality software.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why This Approach Works&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Several important lessons emerged from the workshop.&lt;br&gt;
Requirements Quality Drives AI Quality&lt;br&gt;
The quality of the output is directly related to the quality of the requirements provided.&lt;br&gt;
Clear business requirements consistently produce:&lt;br&gt;
    • Better implementation plans&lt;br&gt;
    • Better test coverage&lt;br&gt;
    • Better code quality&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Human Oversight Remains Critical&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI can accelerate analysis, planning, testing, and coding.&lt;br&gt;
It cannot replace engineering judgment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Developers remain responsible for:&lt;/strong&gt;&lt;br&gt;
    • Understanding business intent&lt;br&gt;
    • Challenging assumptions&lt;br&gt;
    • Evaluating architecture&lt;br&gt;
    • Ensuring quality&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TDD Complements AI Extremely Well&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Generating tests before implementation creates a powerful partnership between developers and AI agents.&lt;br&gt;
Tests provide structure, define expectations, and reduce ambiguity.&lt;br&gt;
The combination of AI-generated tests and human review proved particularly effective.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Process Matters More Than the Model&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the most surprising lessons was that success depended less on the specific AI model being used and more on the workflow surrounding it.&lt;br&gt;
Teams often focus on finding the "best" model.&lt;br&gt;
In reality, a disciplined process consistently delivered better results than relying on model capabilities alone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Evolving Role of Developers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Perhaps the most significant takeaway is that software engineering is evolving.&lt;br&gt;
Developers are spending less time writing repetitive code and more time:&lt;br&gt;
    • Understanding business problems&lt;br&gt;
    • Evaluating solutions&lt;br&gt;
    • Reviewing AI-generated outputs&lt;br&gt;
    • Designing systems&lt;br&gt;
    • Defining quality standards&lt;br&gt;
    • Making architectural decisions&lt;br&gt;
The most valuable engineering skill is no longer typing code quickly.&lt;br&gt;
It is providing the right context, asking the right questions, and validating the right outcomes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The future of software development is not about replacing engineers with AI.&lt;br&gt;
It is about creating an effective partnership between human expertise and AI capabilities.&lt;br&gt;
The workshop demonstrated that successful AI adoption begins long before code generation. It starts with understanding requirements, providing rich context, challenging assumptions, reviewing plans, validating tests, and maintaining ownership of quality throughout the development lifecycle.&lt;br&gt;
When combined with a disciplined workflow, AI becomes more than a productivity tool—it becomes a force multiplier for engineering teams.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>software</category>
      <category>softwaredevelopment</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Enabling CSRF in a JWT-Based React + Spring Boot Application (Enterprise-Ready Approach)</title>
      <dc:creator>Aswani Nayak</dc:creator>
      <pubDate>Wed, 27 May 2026 13:52:27 +0000</pubDate>
      <link>https://dev.to/aswani_nayak_cdcb2fe8ee38/enabling-csrf-in-a-jwt-based-react-spring-boot-application-enterprise-ready-approach-8a0</link>
      <guid>https://dev.to/aswani_nayak_cdcb2fe8ee38/enabling-csrf-in-a-jwt-based-react-spring-boot-application-enterprise-ready-approach-8a0</guid>
      <description>&lt;p&gt;Modern applications often use:&lt;br&gt;
&lt;strong&gt;•   React SPA (Single Page Application)&lt;br&gt;
• Spring Boot backend&lt;br&gt;
• JWT-based authentication&lt;br&gt;
• Stateless REST APIs&lt;/strong&gt;&lt;br&gt;
In this architecture, it’s common to disable CSRF:&lt;br&gt;
&lt;strong&gt;Code&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;.csrf(AbstractHttpConfigurer::disable)&lt;/code&gt;&lt;br&gt;
However, many enterprise security teams and tools (e.g., GitHub CodeQL) flag this as a &lt;strong&gt;high-severity issue&lt;/strong&gt;.&lt;br&gt;
This blog explains:&lt;br&gt;
• ✅ Why CSRF is flagged&lt;br&gt;
• ✅ Whether JWT APIs need CSRF&lt;br&gt;
• ✅ How to enable CSRF without breaking your SPA&lt;br&gt;
• ✅ A production-ready implementation&lt;/p&gt;

&lt;p&gt;1️⃣ &lt;strong&gt;Understanding the Problem&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;What is CSRF?&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;CSRF (Cross-Site Request Forgery)&lt;/strong&gt; is an attack where a malicious website tricks a browser into making an authenticated request to another site.&lt;br&gt;
CSRF attacks rely on:&lt;br&gt;
• Automatic cookie sending by the browser&lt;br&gt;
• Session-based authentication&lt;br&gt;
Spring Security enables CSRF by default for this reason.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why JWT APIs Typically Disable CSRF&lt;/strong&gt;&lt;br&gt;
In a JWT-based system:&lt;br&gt;
• Authentication is done via:&lt;br&gt;
• Authorization: Bearer &lt;br&gt;
• Browsers do NOT automatically attach Authorization headers.&lt;br&gt;
• No session cookies are involved.&lt;br&gt;
• The API is stateless.&lt;br&gt;
Therefore, CSRF protection is &lt;strong&gt;not strictly required&lt;/strong&gt;.&lt;br&gt;
However...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Security Teams Still Want It Enabled&lt;/strong&gt;&lt;br&gt;
Enterprise AppSec teams often require:&lt;br&gt;
• Defense-in-depth&lt;br&gt;
• Uniform policy enforcement&lt;br&gt;
• Future-proof protection (in case cookies are introduced later)&lt;br&gt;
• Clean security scans without exceptions&lt;br&gt;
Instead of disabling CSRF, we can implement it correctly for SPA.&lt;/p&gt;

&lt;p&gt;2️⃣ &lt;strong&gt;Enterprise-Ready Solution&lt;/strong&gt;&lt;br&gt;
We will:&lt;br&gt;
• ✅ Keep JWT authentication&lt;br&gt;
• ✅ Keep backend stateless&lt;br&gt;
• ✅ Enable CSRF&lt;br&gt;
• ✅ Make it work with React SPA&lt;br&gt;
• ✅ Pass security review&lt;br&gt;
The key is:&lt;br&gt;
&lt;strong&gt;Code&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;CookieCsrfTokenRepository&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;3️⃣ &lt;strong&gt;Spring Boot Implementation&lt;/strong&gt;&lt;br&gt;
✅ &lt;strong&gt;Step 1: Security Configuration&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final JwtAuthenticationFilter jwtAuthenticationFilter;

    public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
        this.jwtAuthenticationFilter = jwtAuthenticationFilter;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http
            // ✅ Enable CSRF with Cookie repository (SPA-friendly)
            .csrf(csrf -&amp;gt; csrf
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            )

            // ✅ Keep application stateless
            .sessionManagement(session -&amp;gt;
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )

            .authorizeHttpRequests(auth -&amp;gt; auth
                .requestMatchers("/auth/**").permitAll()
                .anyRequest().authenticated()
            )

            // ✅ Add JWT filter
            .addFilterBefore(jwtAuthenticationFilter,
                    UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;What This Does&lt;/strong&gt;&lt;br&gt;
Spring Security will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Generate a CSRF token&lt;/li&gt;
&lt;li&gt; Send it in a cookie named:
XSRF-TOKEN&lt;/li&gt;
&lt;li&gt; Expect the frontend to send it back in header:
X-XSRF-TOKEN
This pattern is SPA-friendly and secure.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;4️⃣ &lt;strong&gt;React Implementation&lt;/strong&gt;&lt;br&gt;
✅ &lt;strong&gt;Step 1: Enable Credentials&lt;/strong&gt;&lt;br&gt;
When using cookies, you must enable credentials:&lt;br&gt;
&lt;strong&gt;Code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const api = axios.create({
  baseURL: "http://localhost:8080",
  withCredentials: true
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Step 2: Read CSRF Cookie&lt;/strong&gt;&lt;br&gt;
Helper function:&lt;br&gt;
&lt;strong&gt;Code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function getCookie(name) {
  const value = `; ${document.cookie}`;
  const parts = value.split(`; ${name}=`);
  if (parts.length === 2) return parts.pop().split(';').shift();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Step 3: Axios Interceptor&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;api.interceptors.request.use((config) =&amp;gt; {

  const jwt = localStorage.getItem("jwt");
  const csrfToken = getCookie("XSRF-TOKEN");

  if (jwt) {
    config.headers.Authorization = `Bearer ${jwt}`;
  }

  if (csrfToken) {
    config.headers["X-XSRF-TOKEN"] = csrfToken;
  }

  return config;
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every request sends:&lt;br&gt;
• ✅ JWT token&lt;br&gt;
• ✅ CSRF token&lt;/p&gt;

&lt;p&gt;5️⃣ &lt;strong&gt;CORS Configuration (Critical)&lt;/strong&gt;&lt;br&gt;
When using cookies, you must allow credentials.&lt;br&gt;
&lt;strong&gt;Code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(List.of("http://localhost:3000"));
    configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
    configuration.setAllowedHeaders(
        List.of("Authorization", "Content-Type", "X-XSRF-TOKEN")
    );
    configuration.setAllowCredentials(true);

    UrlBasedCorsConfigurationSource source =
            new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And enable CORS:&lt;br&gt;
&lt;strong&gt;Code&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;http.cors(Customizer.withDefaults());&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;6️⃣ &lt;strong&gt;Request Flow Explained&lt;/strong&gt;&lt;br&gt;
✅ &lt;strong&gt;Initial Request&lt;/strong&gt;&lt;br&gt;
• Backend generates CSRF token&lt;br&gt;
• Sends XSRF-TOKEN cookie&lt;br&gt;
✅ &lt;strong&gt;API Call&lt;/strong&gt;&lt;br&gt;
React sends:&lt;br&gt;
Authorization: Bearer &lt;br&gt;
X-XSRF-TOKEN: &lt;br&gt;
✅ &lt;strong&gt;Spring Validates&lt;/strong&gt;&lt;br&gt;
• JWT signature&lt;br&gt;
• CSRF token match&lt;br&gt;
If both valid → request succeeds.&lt;/p&gt;

&lt;p&gt;7️⃣ Architecture Summary&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faw0q0k8cjgg3ax95aapo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faw0q0k8cjgg3ax95aapo.png" alt=" " width="168" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;8️⃣ &lt;strong&gt;Why This Is a Strong Design&lt;/strong&gt;&lt;br&gt;
Even though JWT Authorization header authentication does not require CSRF, enabling it provides:&lt;br&gt;
• Defense-in-depth&lt;br&gt;
• Protection against future cookie-based changes&lt;br&gt;
• Compliance with security standards&lt;br&gt;
• Cleaner audit results&lt;br&gt;
This approach balances:&lt;br&gt;
✅ Security&lt;br&gt;
✅ Architecture purity&lt;br&gt;
✅ Enterprise compliance&lt;/p&gt;

&lt;p&gt;9️⃣ &lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;br&gt;
If you are building:&lt;br&gt;
• React SPA&lt;br&gt;
• Spring Boot backend&lt;br&gt;
• JWT authentication&lt;br&gt;
And your security team requires CSRF to be enabled:&lt;br&gt;
✅ Use CookieCsrfTokenRepository&lt;br&gt;
✅ Send CSRF token via header&lt;br&gt;
✅ Keep the application stateless&lt;br&gt;
This gives you:&lt;br&gt;
• Modern architecture&lt;br&gt;
• Strong security posture&lt;br&gt;
• Enterprise-ready implementation&lt;/p&gt;

</description>
      <category>react</category>
      <category>security</category>
      <category>springboot</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Storing Personal Information in React: sessionStorage vs Context API</title>
      <dc:creator>Aswani Nayak</dc:creator>
      <pubDate>Mon, 18 May 2026 02:25:35 +0000</pubDate>
      <link>https://dev.to/aswani_nayak_cdcb2fe8ee38/storing-personal-information-in-react-sessionstorage-vs-context-api-25pa</link>
      <guid>https://dev.to/aswani_nayak_cdcb2fe8ee38/storing-personal-information-in-react-sessionstorage-vs-context-api-25pa</guid>
      <description>&lt;p&gt;When building React applications, developers often need to handle personal information such as names, emails, profile data, or authentication details.&lt;br&gt;
A common question arises:&lt;br&gt;
Should I store personal information in sessionStorage or use React’s Context API?&lt;br&gt;
The answer depends heavily on security, persistence needs, and application design. In this post, we’ll break down the differences, risks, and best practices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding the Two Options&lt;br&gt;
1️⃣ What Is sessionStorage?&lt;/strong&gt;&lt;br&gt;
sessionStorage is a browser-based storage mechanism that:&lt;br&gt;
• Stores data per tab&lt;br&gt;
• Persists across page refreshes&lt;br&gt;
• Clears when the tab is closed&lt;br&gt;
• Is accessible via JavaScript&lt;br&gt;
Example:&lt;br&gt;
Code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Store data&lt;/span&gt;
&lt;span class="nx"&gt;sessionStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userEmail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Retrieve data&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sessionStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;userEmail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2️⃣ What Is React Context API?&lt;/strong&gt;&lt;br&gt;
The Context API is a React feature that allows you to:&lt;br&gt;
• Share state across components&lt;br&gt;
• Store data in memory&lt;br&gt;
• Avoid prop drilling&lt;br&gt;
• Reset data on page refresh&lt;br&gt;
Example:&lt;br&gt;
Code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UserContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;UserContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Key Differences&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fov25hwjnk9uawpwwsmhe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fov25hwjnk9uawpwwsmhe.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Security Considerations (Critical for Personal Info)&lt;/strong&gt;&lt;br&gt;
When storing personal information (PII), security should be your top priority.&lt;br&gt;
🔴 &lt;strong&gt;Risks of Using sessionStorage&lt;/strong&gt;&lt;br&gt;
• Accessible via browser DevTools&lt;br&gt;
• Accessible to any script running on the page&lt;br&gt;
• Vulnerable to Cross-Site Scripting (XSS)&lt;br&gt;
• Persists after refresh (increasing exposure window)&lt;br&gt;
If your app suffers from an XSS vulnerability, malicious code could easily run:&lt;br&gt;
&lt;strong&gt;Code&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;sessionStorage.getItem("userSSN");&lt;/code&gt;&lt;br&gt;
That’s a serious risk.&lt;/p&gt;




&lt;p&gt;🟢 &lt;strong&gt;Why Context API Is Safer&lt;/strong&gt;&lt;br&gt;
Context data:&lt;br&gt;
• Lives only in memory&lt;br&gt;
• Disappears on refresh&lt;br&gt;
• Is scoped within your React app&lt;br&gt;
• Is not automatically accessible globally&lt;br&gt;
However, note:&lt;br&gt;
Context is still vulnerable to XSS if your app is compromised.&lt;br&gt;
No client-side storage is 100% secure.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;When Should You Use Each?&lt;/strong&gt;&lt;br&gt;
✅ &lt;strong&gt;Use Context API When:&lt;/strong&gt;&lt;br&gt;
• Handling sensitive personal information&lt;br&gt;
• Data does not need to survive page refresh&lt;br&gt;
• You want better encapsulation&lt;br&gt;
• Managing user session state&lt;br&gt;
Examples:&lt;br&gt;
• User profile details&lt;br&gt;
• Temporary form data&lt;br&gt;
• Authenticated user state&lt;/p&gt;




&lt;p&gt;✅ Use sessionStorage When:&lt;br&gt;
• You need data to persist across refresh&lt;br&gt;
• The data is low sensitivity&lt;br&gt;
• UX depends on temporary persistence&lt;br&gt;
Examples:&lt;br&gt;
• Wizard step progress&lt;br&gt;
• Non-sensitive UI preferences&lt;br&gt;
• Temporary search filters&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What You Should NEVER Store in sessionStorage&lt;/strong&gt;&lt;br&gt;
Avoid storing:&lt;br&gt;
• Passwords&lt;br&gt;
• Social Security Numbers&lt;br&gt;
• Credit card numbers&lt;br&gt;
• Medical data&lt;br&gt;
• Raw authentication tokens&lt;br&gt;
• Sensitive PII&lt;br&gt;
If the data would be catastrophic if exposed — don’t store it client-side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Best Practice Architecture&lt;/strong&gt;&lt;br&gt;
For handling personal information securely in modern React apps:&lt;br&gt;
✅ &lt;strong&gt;Recommended Approach&lt;/strong&gt;&lt;br&gt;
• Store authentication tokens in &lt;strong&gt;HTTP-only secure cookies&lt;/strong&gt;&lt;br&gt;
• Store user profile data in &lt;strong&gt;React Context (memory)&lt;/strong&gt;&lt;br&gt;
• Keep highly sensitive data on the &lt;strong&gt;backend only&lt;/strong&gt;&lt;br&gt;
Architecture flow:&lt;br&gt;
Backend&lt;br&gt;
   ↓&lt;br&gt;
HTTP-only cookie (auth token)&lt;br&gt;
   ↓&lt;br&gt;
React Context (user state in memory)&lt;br&gt;
Why this works:&lt;br&gt;
• HTTP-only cookies cannot be accessed via JavaScript&lt;br&gt;
• Context keeps sensitive UI data ephemeral&lt;br&gt;
• Exposure window is minimized&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final Recommendation&lt;/strong&gt;&lt;br&gt;
If you're choosing between sessionStorage and Context API for personal information:&lt;br&gt;
✅ Use &lt;strong&gt;React Context&lt;/strong&gt; for sensitive data&lt;br&gt;
⚠️ Use sessionStorage only for low-risk persistence needs&lt;br&gt;
And remember:&lt;br&gt;
The most secure data is the data you never store on the client.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>security</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
