- Lab
- Core Tech

Guided: Secure User Authentication with JWT in Full-stack Java with Spring Boot 3 and React
Build real world security skills by developing a full stack Java application with robust user authentication from the ground up. In this guided Code Lab, you’ll implement secure login and registration using JSON Web Tokens (JWT), Spring Boot 3, and React. Going step-by-step, you will learn how to protect APIs, manage user sessions without compromising security, and build a modern authentication flow trusted by technology teams. New to authentication? Looking to develop your secure coding skills? This hands-on lab will give you practical experience that’s immediately applicable to enterprise level applications. At the end, you'll have a working application, a solid grasp of JWT based security, and confidence in building secure full-stack solutions. This is perfect for aspiring full stack developers, security conscious engineers, and anyone looking to understand modern web authentication in action.

Path Info
Table of Contents
-
Challenge
Backend: Security Configuration
A chain in web security refers to a sequence of Filter instances that process an incoming HTTP request before it reaches the main application logic. These filters are executed one after another in a defined order.
Each Filter performs a specific task, such as authentication, authorization, logging, input validation, or modifying request data. This allows security and request processing logic to be handled separately from business logic.
When a request arrives, it passes through the filter chain. Each filter can inspect or change the request, block it, or pass it on to the next filter. Only if the request passes all checks will it reach the intended endpoint.
This approach helps create clean, modular, and secure web applications by centralizing cross-cutting concerns like security and monitoring.
info > If you get stuck, there are files with completed code for each task located in the
solution
folder in your filetree. The methodgenerateToken(UserDetails userDetails)
is a shortcut that creates aJWT
(JSON Web Token) for a user without any extra claims. It internally calls another method that accepts both custom claims and user details, passing in an empty map to indicate no additional claims. This simplifies token generation when only basic user information is needed. Allowed methods refer to the specific HTTP actions such asGET
,POST
,PUT
, andDELETE
that a server explicitly permits for a given endpoint or resource. Each of these methods serves a different purpose: for example,GET
is used to retrieve data,POST
to create new data,PUT
to update existing data, andDELETE
to remove data.When a client (like a web browser or mobile app) makes a request to the server, it must use one of the methods that the server allows for that particular endpoint. If the client tries to use a method that is not supported for example, sending a
DELETE
request to an endpoint that only allowsGET
andPOST
the server will reject the request and respond with a405
Method Not Allowed error.This helps enforce proper use of the API and protects the application from unintended or harmful actions.
-
Challenge
Backend: User Management
To improve security and data integrity, it's important to apply validation and uniqueness constraints to user input, especially for critical fields like usernames. In this task, you'll be updating the
User
entity to ensure theusername
field cannot be left blank, is limited to a maximum of 50 characters, and must be unique across all users.These constraints help prevent invalid data from being stored and ensure consistency when processing user registrations or logins. When a user registers, it's important to construct their account securely and cleanly. Using the builder pattern to create a new User object improves code readability and maintainability.
Additionally, encoding the password before saving it helps prevent sensitive information from being stored in plain text, which is a critical security practice. In this task, you’ll implement user registration logic that validates and securely creates a new user with default settings, such as assigning the
ROLE_USER
role. To protect user credentials, passwords should never be stored in plain text. Instead, they should be securely hashed using a strong algorithm like BCrypt. BCrypt is a widely accepted and secure hashing function designed specifically for password storage. By adding a BCrypt password encoder to your application configuration, you ensure that all passwords are encoded before being saved. This setup is a standard security best practice in any modern authentication system. -
Challenge
Backend: Authentication System
To allow users to authenticate securely, you need to create a login endpoint in your application. This endpoint will accept a
POST
request with user credentials, validate them, and then pass the information to theauthenticationService
. If the credentials are valid, the service will generate and return an authentication response, typically including a token. The endpoint will respond with anHTTP 200 OK
status to indicate a successful login. This setup is essential for enabling secure, stateless authentication in modern web applications. In a JWT-based authentication system, users don't send their credentials with every request. Instead, they include a JWT token in the request header. To process this securely, you need a filter that validates the token on each incoming request. This filter checks if the token is valid and, if so, sets up an authenticated security context for the request.This allows Spring Security to recognize the user and authorize access to protected resources without requiring them to log in again. This step is essential to enabling stateless authentication using JWT. To protect sensitive parts of your application, such as
admin
features, you can use role-based access control. In Spring Boot, this is done by securing controller endpoints with annotations like@PreAuthorize
. By restricting certain endpoints to users with specific roles (e.g.,ADMIN
), you ensure that only authorized personnel can access critical functionality. In this task, you'll secure two endpoints under the/api/v1/admin
path so that only users with theADMIN
role can access them. This is a core part of enforcing proper access control within your application. -
Challenge
Frontend: Authentication UI
The login form is a crucial component that allows users to enter their credentials and access the application securely. To do this properly, the form must include input fields for both the username and password, where the password input is specifically marked as
type="password"
to mask the input. In this task, you’ll update the Login.js file to include these fields using Material UI’s<TextField>
component, which provides a consistent and user-friendly input design. To maintain a user’s authenticated session across page refreshes or browser restarts, authentication data must be securely stored in the browser. This includes theJWT
access token (used for API authentication), a refresh token (used to get a new access token when the current one expires), and the user's basic information (e.g. username and role).Storing these items in
localStorage
allows the frontend to persist the login state and manage session continuity. In this task, you’ll update theAuthContext.js
file to store this data after both login and registration events. In a frontend application, protected routes ensure that only authenticated users, or users with specific roles, can access certain pages. This is essential for implementing role-based access control (RBAC). In React, this can be achieved by creating a custom PrivateRoute component that checks the user's authentication and role. If a user lacks the required role, they are redirected to a safe location (e.g., the dashboard), preventing unauthorized access to restricted pages.In this task, you'll secure access to the
/dashboards
route so that only authenticated users with the proper role can view it. -
Challenge
Frontend: User Experience
When building a frontend application with authentication, it’s important to customize the user interface based on the user’s role and login status. This improves both security and user experience.
In this task, you'll update the navigation bar (
Navbar.js
) to conditionally display a link to the Admin Dashboard only when the user is both authenticated and has the roleROLE_ADMIN
. This ensures that only authorized users see and can access admin-related pages, using React Router's Link component and Material UI's Button for styling. An admin dashboard typically displays sensitive or privileged data, so it must be protected and loaded only by authenticated administrators.In this task, you'll update the
AdminDashboard.js
component to fetch data securely from the backend usingAxios
. You'll create an asynchronous function calledfetchDashboardData
, which will request the admin data from a protected API endpoint. If successful, the data will be saved to state for rendering. If it fails, the component will display an error message and log the error to the console for debugging. The user dashboard is a personalized area where users can view content specific to their account. A common and user-friendly feature in multi-user applications is to greet users by name and display their role. This helps confirm their identity and shows that they’re accessing the correct view.In this task, you'll update the
Dashboard.js
file to show a dynamic welcome message using Material UI’s Typography component. It will insert the user's username and role into the message for a simple, effective personalization experience. -
Challenge
Frontend: Security Features
A secure logout function is critical to protecting user data and preventing unauthorized access after a session ends. When a user logs out, all authentication related data such as tokens and user details, must be cleared from the browser. Additionally, any default headers used for API calls, like the Authorization header in Axios, must be removed to stop sending the token in future requests.
In this task, you'll update the
AuthContext.js
file to include a logout function that handles all of these steps, ensuring the session is properly and securely terminated. In a React application, using a context provider is a clean and efficient way to share global state and functions, such as authentication logic, across multiple components. TheAuthContext
allows components anywhere in the app to access the current user, authentication status, and relevant functions like login, register, and logout.In this task, you'll define an object called value that bundles all of this authentication-related state and behavior. This object will be passed to the context provider so other components can easily consume it. Great, with all the tasks complete, Start the application.
- Start the back end. Using the first Terminal tab, start spring-boot, by typing
mvn spring-boot:run --offline
. Note: this will take a moment to start spring-boot running. - Start the front end. Using the second Terminal tab, navigate to to the front end folder by typing
cd frontend
. Then start the front end by typingnpm start
- Once the front end loads, use the Browser tab to navigate to
http://localhost:3000/
, this will load the application in the browser.
Congratulations on coming this far and completing the Code Lab Secure User Authentication with JWT in Full-stack Java with Spring Boot 3 and React!
- Start the back end. Using the first Terminal tab, start spring-boot, by typing
What's a lab?
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Provided environment for hands-on practice
We will provide the credentials and environment necessary for you to practice right within your browser.
Guided walkthrough
Follow along with the author’s guided walkthrough and build something new in your provided environment!
Did you know?
On average, you retain 75% more of your learning if you get time for practice.