Content ITV PRO
This is Itvedant Content department
Business Scenario
In this lab, we will integrate the Medicare React frontend with the existing Spring Boot authentication system by implementing user registration, login, and JWT-based authentication.
Pre-Lab Preparation
Module:
1) React Basics & Backend Integration (Spring Boot)
Register → Name + Email + Phone + Password Role → PATIENT automatically Login → Email + Password ↓ JWT
Phase 12B — Authentication, JWT & Protected Routes
↓ Patient / Doctor Dashboard
POST /api/auth/register POST /api/auth/login
Configure CORS in Spring Boot
1
C:\MEDICAREPROJECT\medicare-backend
http://localhost:8080
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;@Bean
public UrlBasedCorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(
List.of("http://localhost:5173")
);
configuration.setAllowedMethods(
List.of("GET","POST","PUT","PATCH","DELETE","OPTIONS")
);
configuration.setAllowedHeaders(
List.of("Authorization","Content-Type")
);
configuration.setAllowCredentials(false);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",configuration);
return source;
}.cors(cors -> cors.configurationSource(
corsConfigurationSource()
)
)SecurityFilterChain securityFilterChain(...)
JWT filter Authorization rules SessionCreationPolicy.STATELESS CustomAuthenticationEntryPoint CustomAccessDeniedHandler
Check .env
2
medicare-frontend/.env
VITE_API_BASE_URL=http://localhost:8080/api
Create authService.js
3
src/services/authService.js
import api from "../api/api";
const register = async (registerData) => {
const response = await api.post("/auth/register",registerData);
return response.data;
};
const login = async (loginData) => {
const response = await api.post("/auth/login",loginData);
return response.data;
};
const authService = {register,login};
export default authService;Understand Our Login Response
4
{
"token": "eyJhbGciOiJIUzI1NiJ9...",
"tokenType": "Bearer",
"userId": 1,
"name": "Rahul Shah",
"email": "rahul@gmail.com",
"role": "PATIENT"
}{
"token": "eyJhbGciOiJIUzI1NiJ9...",
"tokenType": "Bearer",
"userId": 2,
"name": "Dr. Ananya",
"email": "ananya@gmail.com",
"role": "DOCTOR"
}Create AuthContext
5
src/context/AuthContext.jsx
import {createContext,useContext,useState} from "react";
const AuthContext = createContext(null);
function getStoredUser() {
const storedUser = localStorage.getItem("user");
if (!storedUser) {
return null;
}
try {
return JSON.parse(storedUser); } catch {
localStorage.removeItem("user");
return null;
}
}
export function AuthProvider({ children }) {
const [token, setToken] = useState(() =>
localStorage.getItem("token")
);
const [user, setUser] = useState(getStoredUser);
const login = (authResponse) => {
const loggedInUser = {
userId: authResponse.userId,
name: authResponse.name,
email: authResponse.email,
role: authResponse.role
};
localStorage.setItem("token",authResponse.token);
localStorage.setItem("user",JSON.stringify(loggedInUser));
setToken(authResponse.token);
setUser(loggedInUser);
}; const logout = () => {
localStorage.removeItem("token");
localStorage.removeItem("user"
);
setToken(null);
setUser(null);
};
const isAuthenticated = Boolean(token && user);
return (
<AuthContext.Provider
value={{user,token,isAuthenticated,login,logout
}}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
return useContext( AuthContext);
}Why AuthContext?
Login ↓Patient Dashboard ↓Doctor Search ↓Appointments ↓Profile
AuthContext │ ┌──────────┼──────────┐ ↓ ↓ ↓ Navbar Dashboard Routes
const {
user,
token,
login,
logout,
isAuthenticated
} = useAuth();Why localStorage AND Context?
localStorage ↓ persists JWT + user AuthContext ↓ provides that information through React
Wrap Application with AuthProvider
6
import React from "react";
import ReactDOM from "react-dom/client";
import {RouterProvider} from "react-router/dom";
import "bootstrap/dist/css/bootstrap.min.css";import "bootstrap/dist/js/bootstrap.bundle.min.js";
import "./index.css";
import router from "./routes/router";
import {AuthProvider} from "./context/AuthContext";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<AuthProvider>
<RouterProvider
router={router}/>
</AuthProvider>
</React.StrictMode>
);Build Real Registration Page
7
import { useState } from "react";
import { Link, useNavigate } from "react-router";import authService from "../../services/authService";
function Register() {
const navigate = useNavigate();
const [formData, setFormData] = useState({
name: "",
email: "",
phone: "",
password: ""
});
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const handleChange = (event) => {
const { name, value } = event.target;
setFormData((previous) => ({
...previous,
[name]: value
}));
};
const handleSubmit = async (event) => {
event.preventDefault();
setError("");
setLoading(true);
try {
await authService.register(formData); navigate("/login", {
state: {
message: "Registration successful. Please login."
}
});
} catch (error) {
setError(
error.response?.data?.message || "Registration failed"
);
} finally {
setLoading(false);
}
};
return (
<div className="container py-5">
<div className="row justify-content-center">
<div className="col-md-7 col-lg-6">
<div className="card shadow-sm">
<div className="card-body p-4">
<h2 className="text-center mb-4">Create Account</h2>
{error && (
<div className="alert alert-danger">
{error}
</div>
)}
<form onSubmit={handleSubmit}>
<div className="mb-3"> <label className="form-label">Name</label>
<input
type="text"
name="name"
className="form-control"
value={formData.name}
onChange={handleChange}
required
/>
</div>
<div className="mb-3">
<label className="form-label">Email</label>
<input
type="email"
name="email"
className="form-control"
value={formData.email}
onChange={handleChange}
required
/>
</div>
<div className="mb-3">
<label className="form-label">Phone Number</label>
<input
type="tel"
name="phone"
className="form-control"
value={formData.phone} onChange={handleChange}
required
/>
</div>
<div className="mb-3">
<label className="form-label">Password</label>
<input
type="password"
name="password"
className="form-control"
value={formData.password}
onChange={handleChange}
required
/>
</div>
<button
type="submit"
className="btn btn-primary w-100"
disabled={loading}
>
{loading ? "Creating Account..." : "Register"}
</button>
</form>
<p className="text-center mt-4 mb-0">
Already have an account?{" "}
<Link to="/login">Login</Link>
</p> </div>
</div>
</div>
</div>
</div>
);
}
export default Register;Very Important — No Role Field
Role: [ PATIENT ] [ DOCTOR ]
{ "role": "DOCTOR" }{
"name": "Rahul Shah",
"email": "rahul@gmail.com",
"phone": "9876543210",
"password": "password"
}Build Real Login Page
8
import { useState } from "react";
import { Link, useLocation, useNavigate } from "react-router";
import authService from "../../services/authService";
import { useAuth } from "../../context/AuthContext";
function Login() {
const navigate = useNavigate();
const location = useLocation();
const { login } = useAuth();
const [formData, setFormData] = useState({
email: "",
password: ""
});
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const handleChange = (event) => {
const { name, value } = event.target;
setFormData((previous) => ({
...previous,
[name]: value
}));
};
const handleSubmit = async (event) => {
event.preventDefault();
setError("");
setLoading(true); try {
const response = await authService.login(formData);
login(response);
if (response.role === "PATIENT") {
navigate("/patient/dashboard");
} else if (response.role === "DOCTOR") {
navigate("/doctor/dashboard");
} else {
navigate("/unauthorized");
}
} catch (error) {
setError(
error.response?.data?.message || "Invalid email or password"
);
} finally {
setLoading(false);
}
};
return (
<div className="container py-5">
<div className="row justify-content-center">
<div className="col-md-6 col-lg-5">
<div className="card shadow-sm">
<div className="card-body p-4">
<h2 className="text-center mb-4">Login</h2>
{location.state?.message && (
<div className="alert alert-success"> {location.state.message}
</div>
)}
{error && (
<div className="alert alert-danger">
{error}
</div>
)}
<form onSubmit={handleSubmit}>
<div className="mb-3">
<label className="form-label">Email</label>
<input
type="email"
name="email"
className="form-control"
value={formData.email}
onChange={handleChange}
required
/>
</div>
<div className="mb-3">
<label className="form-label">Password</label>
<input
type="password"
name="password"
className="form-control"
value={formData.password} onChange={handleChange}
required
/>
</div>
<button
type="submit"
className="btn btn-primary w-100"
disabled={loading}
>
{loading ? "Logging in..." : "Login"}
</button>
</form>
<p className="text-center mt-4 mb-0">
Don't have an account?{" "}
<Link to="/register">Register</Link>
</p>
</div>
</div>
</div>
</div>
</div>
);
}
export default Login;Create ProtectedRoute
9
import { Navigate } from "react-router";
import { useAuth } from "../context/AuthContext";
function ProtectedRoute({ children, allowedRoles = [] }) {
const { user, isAuthenticated } = useAuth();
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
if (allowedRoles.length > 0 && !allowedRoles.includes(user?.role)) {
return <Navigate to="/unauthorized" replace />;
}
return children;
}
export default ProtectedRoute;Protect Routes
10
import { createBrowserRouter } from "react-router";
import Home from "../pages/common/Home";
import Login from "../pages/auth/Login";
import Register from "../pages/auth/Register";
import PatientDashboard from "../pages/patient/PatientDashboard";
import DoctorDashboard from "../pages/doctor/DoctorDashboard";
import Unauthorized from "../pages/common/Unauthorized";
import NotFound from "../pages/common/NotFound";
import ProtectedRoute from "./ProtectedRoute";
const router = createBrowserRouter([
{
path: "/",
element: <Home />
}, {
path: "/login",
element: <Login />
},
{
path: "/register",
element: <Register />
},
{
path: "/patient/dashboard",
element: (
<ProtectedRoute allowedRoles={["PATIENT"]}>
<PatientDashboard />
</ProtectedRoute>
)
},
{
path: "/doctor/dashboard",
element: (
<ProtectedRoute allowedRoles={["DOCTOR"]}>
<DoctorDashboard />
</ProtectedRoute>
)
},
{
path: "/unauthorized",
element: <Unauthorized />
}, {
path: "*",
element: <NotFound />
}
]);
export default router;What ProtectedRoute Is Doing
Not logged in
/patient/dashboard
/doctor/dashboard
Add Logout to Patient Dashboard
11
import { useNavigate } from "react-router";
import { useAuth } from "../../context/AuthContext";
function PatientDashboard() {
const navigate = useNavigate(); const { user, logout } = useAuth();
const handleLogout = () => {
logout();
navigate("/login");
};
return (
<div className="container py-5">
<div className="d-flex justify-content-between align-items-center mb-4">
<div>
<h2>Patient Dashboard</h2>
<p className="text-secondary mb-0">
Welcome, {user?.name}
</p>
</div>
<button
className="btn btn-outline-danger"
onClick={handleLogout}
>
Logout
</button>
</div>
<div className="alert alert-info">
Patient healthcare modules will be added next.
</div>
</div>
);}
export default PatientDashboard;Add Logout to Doctor Dashboard
12
import { useNavigate } from "react-router";
import { useAuth } from "../../context/AuthContext";
function DoctorDashboard() {
const navigate = useNavigate();
const { user, logout } = useAuth();
const handleLogout = () => {
logout();
navigate("/login");
};
return ( <div className="container py-5">
<div className="d-flex justify-content-between align-items-center mb-4">
<div>
<h2>Doctor Dashboard</h2>
<p className="text-secondary mb-0">
Welcome, {user?.name}
</p>
</div>
<button className="btn btn-outline-danger"
onClick={handleLogout}>
Logout
</button>
</div>
<div className="alert alert-info">
Doctor appointment modules will be added next.
</div>
</div>
);
}
export default DoctorDashboard;Restart Both Applications
13
Test Registration
14
Name: Rahul Shah Email: rahul@gmail.com Phone: 9876543210 Password: your password
{
"name": "Rahul Shah",
"email": "rahul@gmail.com",
"phone": "9876543210",
"password": "your password"
}Test Patient Login
15
{
"email": "rahul@gmail.com",
"password": "your password"
}localStorage token user
PATIENT --> /patient/dashboard
Inspect Browser Storage
16
Application ↓ Local Storage ↓ http://localhost:5173
token : eyJhbGciOiJIUzI1NiJ9...
user : {"userId":1,"name":"Rahul Shah","email":"rahul@gmail.com","role":"PATIENT"}Test Browser Refresh
17
localStorage ↓ AuthContext initialization ↓ token restored user restored
Test Patient Security
18
403 Access Denied
Test Doctor Login
19
Doctor Email Doctor Password
{ "role": "DOCTOR" }
Test Doctor Security
20
Test Logout
21
localStorage.removeItem("token");
localStorage.removeItem("user");user = null token = null
REGISTER │ ▼ Register.jsx │ ▼ authService.js │ ▼ POST /api/auth/register │ ▼ Spring Boot │ role = PATIENT
Complete Authentication Architecture
LOGIN │ ▼ Login.jsx │ ▼ authService.js │ ▼ POST /api/auth/login │ ▼ Spring Security │ ▼ JWT │ ▼
AuthContext │ ┌─────────┴─────────┐ ▼ ▼ Context localStorage │ ▼ ProtectedRoute │ ┌────┴─────┐ ▼ ▼ PATIENT DOCTOR │ │ ▼ ▼ Patient Doctor Dashboard Dashboard
We are done with this lab. The latest source code has been uploaded to GitHub. You can access the latest commit using the link below:
Great job!
You have successfully implemented the Medicare frontend authentication workflow using React, JWT, and protected routes.
Checkpoint
Git Push
git push origin branchNameNext-Lab Preparation
Module:
1) React Basics & Backend Integration (Spring Boot)
By Content ITV