Logo

dev-resources.site

for different kinds of informations.

Implementing JWT Authentication in .NET API

Published at
1/8/2025
Categories
jwt
net
Author
stackpuz
Categories
2 categories in total
jwt
open
net
open
Author
8 person written this
stackpuz
open
Implementing JWT Authentication in .NET API

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
Enter fullscreen mode Exit fullscreen mode

Project structure

โ”œโ”€ App.csproj
โ”œโ”€ appsettings.json
โ”œโ”€ Program.cs
โ”œโ”€ Controllers
โ”‚  โ””โ”€ AppController.cs
โ””โ”€ wwwroot
   โ”œโ”€ index.html
   โ””โ”€ login.html
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Open the web browser and goto http://localhost:5122

You will find this test page.

test page

Testing

After a few seconds, you will be redirected to the login page.

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.

logged in

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.

login page

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

net Article's
30 articles in total
Favicon
Implementing JWT Authentication in .NET API
Favicon
ASP.NET MVC Suite Update: Aligning with .NET Changes
Favicon
Building a React CRUD App with a .NET API
Favicon
Exploring Records in C#.
Favicon
Stack vs Heap in C#: Key Differences and Usage
Favicon
DLL injection of managed code into native process
Favicon
Syncfusion Visual Studio Extensions Are Now Compatible With .NET 9.0
Favicon
Syncfusion Now Supports .NET 9!
Favicon
Open Source Tools for .NET MAUI
Favicon
Introducing Syncfusionโ€™s First Set of Open-Source .NET MAUI Controls
Favicon
Bootcamp De Backend Com .NET Gratuito DIO + Randstad
Favicon
Building a Vue CRUD App with a .NET API
Favicon
Building an Angular CRUD App with a .NET API
Favicon
Bootcamp De Backend Com .NET Gratuito De DIO + Randstad
Favicon
Unlock Efficient Data Exchange: Import & Export Excel in ASP.NET Core
Favicon
How I Access the Dark Web Using This Search Engine ๐Ÿ”ฎ
Favicon
Building a File Upload API in .NET
Favicon
EF Core 6 - This SqlTransaction has completed; it is no longer usable.
Favicon
Implement data validation in .NET
Favicon
Create a pagination API with .NET
Favicon
Create an API for DataTables with .NET
Favicon
Getting Started with .NET Aspire: Simplifying Cloud-Native Development
Favicon
Create a CRUD API with .NET
Favicon
Cultivating Trust and Innovation: Top 10 .NET Development Partners You Can Rely On
Favicon
Bootcamp De .NET Gratuito Com Oportunidade De Contrataรงรฃo
Favicon
Dapper mappings, which is best?
Favicon
Reading Request Headers Across Multiple .NET Core API Controllers
Favicon
Understanding DynamicData in .NET: Reactive Data Management Made Easy
Favicon
The Future of .NET Development: Skills and Expertise Needed in Tomorrow's Programmers
Favicon
๐Ÿฆ™ Harnessing Local AI: Unleashing the Power of .NET Smart Components and Llama2

Featured ones: