dev-resources.site
for different kinds of informations.
Implementing JWT Authentication in .NET API
JWT (JSON Web Token) is a popular and efficient method for securing APIs by implementing token-based authentication, ensuring that only authorized users can access your API endpoints. Unlike traditional session-based methods, JWT is stateless and doesn't require storing user sessions on the server, making it ideal for scalable applications. In this guide, we'll explore how to implement JWT authentication in a .NET API, from generating tokens upon user login to securing endpoints by validating these tokens, ultimately enhancing the security of your application's data and services.
Prerequisites
- .NET 8
Setup project
dotnet new webapi -o dotnet_api -n App
Project structure
โโ App.csproj
โโ appsettings.json
โโ Program.cs
โโ Controllers
โ โโ AppController.cs
โโ wwwroot
โโ index.html
โโ login.html
Project files
App.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.3" />
</ItemGroup>
</Project>
TheApp.csproj
file includes a reference to the Microsoft.AspNetCore.Authentication.JwtBearer
package, which is used to implement JWT authentication in the application.
appsettings.json
{
"jwt_secret": "b0WciedNJvFCqFRbB2A1QhZoCDnutAOen5g1FEDO0HsLTwGINp04GXh2OXVpTqQL"
}
The appsettings.json
file containing a jwt_secret
property. It is a string used to sign and verify JWT tokens in the application.
AppController.cs
using System.Security.Claims;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Mvc;
namespace App.Controllers
{
public class AppController : Controller
{
private readonly IConfiguration _configuration;
public AppController(IConfiguration configuration)
{
_configuration = configuration;
}
[HttpGet("user")]
public IActionResult GetUser()
{
if (User.Identity.IsAuthenticated) {
return Ok(new
{
name = User.Identity.Name
});
}
return StatusCode(401);
}
[HttpPost("Login")]
public IActionResult Login([FromBody] Dictionary<string, string> login)
{
if (login["name"] == "admin" && login["password"] == "1234") {
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, "1"),
new Claim(ClaimTypes.Name, "admin")
};
var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(_configuration["jwt_secret"]));
var token = new JwtSecurityToken(
expires: DateTime.Now.AddHours(24),
claims: claims,
signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
);
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(token),
});
}
return StatusCode(400);
}
}
}
The AppController.cs
file defines two endpoints for JWT authentication in an ASP.NET Core API. The GetUser
method checks if the user is authenticated and returns the user's name if so. The Login
method validates user credentials and, if correct, generates a JWT containing the user's claims (name and identifier), signs it using a symmetric key, and returns the token. The JWT is configured to expire in 24 hours.
Program.cs
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false,
ValidateAudience = false,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["jwt_secret"]))
};
});
var app = builder.Build();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.MapControllers();
app.Run();
The Program.cs
file configures JWT authentication for an ASP.NET Core application. It sets up authentication using the JwtBearerDefaults.AuthenticationScheme
and configures JWT bearer token handling. The AddJwtBearer
method specifies the token validation parameters, including disabling issuer and audience validation, and using a symmetric signing key derived from the jwt_secret
stored in the application's configuration. The app uses UseAuthentication()
to ensure that the authentication middleware is applied, enabling token-based authentication for protected routes. The app is set to serve static files and map controllers for routing.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row">
<div class="col mt-5">
<div class="d-flex justify-content-center">
<div class="card p-0">
<div class="card-header">
<h3>Home</h3>
</div>
<div class="card-body text-center">
<i id="icon" class="fa fa-times-circle fa-5x mt-3 text-danger"></i>
<p id="message" class="mt-3">
You are currently not logged in.
<br/>
Redirecting to login page in seconds..
</p>
<div id="logout" class="col-12 d-none">
<button class="btn btn-primary w-100" onclick="logout()">Logout</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function init() {
fetch('/user', {
headers: {
Authorization: 'Bearer ' + localStorage.getItem('token')
}
}).then(response => {
if (response.ok) {
response.json().then(data => {
document.getElementById('icon').className = 'fa fa-check-circle fa-5x mt-3 text-success'
document.getElementById('message').innerText = 'You are currently logged in as: ' + data.name
document.getElementById('logout').classList.remove('d-none')
})
}
else {
if (response.status === 401) {
setTimeout(() => {
location = '/login.html'
}, 4000)
}
else {
alert(`Error: ${response.status} ${response.statusText}`)
}
}
})
}
function logout() {
localStorage.removeItem('token')
location.reload()
}
init()
</script>
</body>
The index.html
is a simple web page that provides a user interface for displaying the login status of a user. It uses Bootstrap for styling and Font Awesome for icons. On page load, it checks the user's authentication status by sending a request to the server with a JWT token stored in localStorage
. If the user is logged in, it shows a success message with the user's name and a logout button. If not logged in, it shows a message indicating the user is not logged in and redirects them to the login page after a few seconds.
login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row">
<div class="col mt-5">
<div class="d-flex justify-content-center">
<div class="card p-0" style="width: 300px;">
<div class="card-header">
<h3>Login</h3>
</div>
<div class="card-body">
<i class="fa fa-user-circle fa-5x d-block text-center text-secondary"></i>
<form onsubmit="return login()">
<div class="row">
<div class="mb-3 col-12">
<label class="form-label" for="user_account_name">User Name</label>
<input id="name" class="form-control form-control" value="admin" required/>
</div>
<div class="mb-3 col-12">
<label class="form-label" for="user_account_password">Password</label>
<input id="password" class="form-control form-control" type="password" value="1234" required/>
</div>
<div class="col-12">
<button class="btn btn-primary w-100">Login</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function login() {
fetch('/login', {
method: 'POST',
body: JSON.stringify({
name: document.getElementById('name').value,
password: document.getElementById('password').value
}),
headers: {
'Content-Type': 'application/json'
}
}).then(response => {
if (response.ok) {
response.json().then(data => {
localStorage.setItem('token', data.token)
location = '/'
})
}
else {
alert(`Error: ${response.status} ${response.statusText}`)
}
})
return false
}
</script>
</body>
The login.html
page provides a simple login form where users can input their username and password. It uses Bootstrap for styling and Font Awesome for icons. When the user submits the form, a JavaScript function login()
sends a POST
request to the/login
endpoint with the entered credentials. If the login is successful, the server returns a JWT token, which is stored in localStorage
. The page then redirects the user to the home page (/
). If the login fails, an error message is displayed.
Run project
dotnet run
Open the web browser and goto http://localhost:5122
You will find this test page.
Testing
After a few seconds, you will be redirected to the login page.
Press the login button, and you will be logged in to the home page, which will display the logged-in user's name.
Try refreshing the browser, and you will see that you're still logged in. Then, press the logout button, the JWT token will be removed, and you will be redirected to the login page again.
Conclusion
In conclusion, implementing JWT authentication in a .NET API is a straightforward process that leverages the Microsoft.AspNetCore.Authentication.JwtBearer
package. By configuring the JWT bearer authentication scheme in Program.cs
and utilizing JwtSecurityToken
in controllers for token generation, you can secure your API endpoints effectively. The key steps include validating the token, ensuring the correct signing key is used, and managing token expiration. With this setup, you can easily integrate token-based authentication, allowing secure access to your API resources.
Source code: https://github.com/stackpuz/Example-JWT-dotnet-8
Create a CRUD Web App in Minutes: https://stackpuz.com
Featured ones: