In this article, we continue developing the blog-type application, focusing on implementing authentication and authorization. We will use:
-
ASP.NET Core Identity for user management
-
JWT (JSON Web Tokens) for stateless authentication
-
React Context + localStorage to maintain authentication on the frontend
-
Protecting routes and API calls based on authentication
🧩 Why JWT? Why stateless?
JWT-based authentication is stateless, which means the server does not need to store sessions. The signed token is sufficient to prove the user's identity. Advantages:
-
Increased scalability
-
Ideal for SPA applications (React, Angular)
-
Easy to extend with role/permission-based authorization
1️⃣ Identity + JWT in .NET Core
✅ Package installation
Add to the WebAPI project:
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
✅ Configuration in Program.cs
builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "ludo.blog",
ValidAudience = "ludo.blog",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your_secret_key_123456"))
};
});
🔐 Make sure the JWT secret is saved in appsettings.json or in UserSecrets.
✅ Token generation
private string GenerateJwtToken(ApplicationUser user)
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(ClaimTypes.NameIdentifier, user.Id)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtConfig.Secret));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _jwtConfig.Issuer,
audience: _jwtConfig.Audience,
claims: claims,
expires: DateTime.Now.AddHours(2),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
2️⃣ Login in React + Token storage
✅ API call from React
const login = async (email: string, password: string) => {
try {
const res = await axios.post("/api/auth/login", { email, password });
localStorage.setItem("token", res.data.token);
setUser(jwtDecode(res.data.token)); // decode token & extract user info
} catch (err) {
setError("Login failed!");
}
};
✅ AuthContext
export const AuthContext = createContext<AuthState>(null);
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
useEffect(() => {
const token = localStorage.getItem("token");
if (token) setUser(jwtDecode(token));
}, []);
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
};
3️⃣ Protecting routes in React
✅ PrivateRoute
const PrivateRoute = ({ children }) => {
const { user } = useAuth();
return user ? children : <Navigate to="/login" />;
};
✅ Usage in App.tsx
<Routes>
<Route path="/posts" element={<PrivateRoute><UserPosts /></PrivateRoute>} />
<Route path="/login" element={<LoginPage />} />
</Routes>
4️⃣ Displaying personal posts
✅ Backend: Secured endpoint
[Authorize]
[HttpGet("me")]
public async Task<IActionResult> GetMyPosts()
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var posts = await _postService.GetPostsByUser(userId);
return Ok(posts);
}
✅ Frontend: Call with token
const token = localStorage.getItem("token");
const res = await axios.get("/api/posts/me", {
headers: { Authorization: `Bearer ${token}` }
});
✅ Conclusion
We have implemented a complete authentication and authorization flow for our blog application. The JWT token allows secure communication between frontend and backend, and React can manage the session using Context API and localStorage. In the next articles, we will extend this system with registration, refresh tokens, role-based authorization, and a customized UI for logged-in users.