Angular 20

Build a Secure ASP.NET Core Web API with SQL Server, EF Core, JWT Authentication & Image Upload

Β Build a Secure ASP.NET Core REST API with SQL Server, EF Core, JWT Authentication & Image Upload

In this comprehensive tutorial, we’ll walk you through building a full-featured RESTful API using ASP.NET Core 8 with SQL Server, Entity Framework Core, and support for image uploads. We’ll also implement JWT Token Authentication so users can register, login, and access protected endpoints securely.

🧰 Step 1: Create ASP.NET Core Web API Project

Let’s start by creating a new API project using the CLI.

dotnet new webapi -n DotNetJwtApi

πŸ“ Recommended Project Structure

DotNetJwtApi/
β”œβ”€β”€ Controllers/
β”‚   └── UsersController.cs
β”œβ”€β”€ Models/
β”‚   β”œβ”€β”€ User.cs
β”‚   β”œβ”€β”€ UserDto.cs
β”‚   └── LoginDto.cs
β”œβ”€β”€ Services/
β”‚   └── TokenService.cs
β”œβ”€β”€ Data/
β”‚   └── ApplicationDbContext.cs
β”œβ”€β”€ wwwroot/
β”‚   └── uploads/
β”œβ”€β”€ Program.cs
β”œβ”€β”€ appsettings.json
└── ...
  

πŸ“¦ Step 2: Install Required NuGet Packages

dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package System.IdentityModel.Tokens.Jwt

🧾 Step 3: Define the User Model

namespace DotNetJwtApi.Models;

public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; } = string.Empty;
    public string LastName { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;
    public string? ImageUrl { get; set; }
}

πŸ“₯ Step 4: UserDto for Register

public class UserDto
{
    [FromForm(Name = "first_name")]
    public string FirstName { get; set; }

    [FromForm(Name = "last_name")]
    public string LastName { get; set; }

    [FromForm(Name = "email")]
    public string Email { get; set; }

    [FromForm(Name = "password")]
    public string Password { get; set; }

    [FromForm(Name = "image")]
    public IFormFile? Image { get; set; }
}

πŸ” Step 5: LoginDto for Login API

public class LoginDto
{
    public string Email { get; set; }
    public string Password { get; set; }
}

πŸ—ƒοΈ Step 6: Setup Database Context

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions options)
        : base(options) { }

    public DbSet<User> Users { get; set; }
}

πŸ”§ Step 7: Add Connection String in appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=JwtApiDb;Trusted_Connection=True;TrustServerCertificate=True;"
  },
  "Jwt": {
    "Key": "ThisIsASuperSecretKeyForJwtTokenGeneration",
    "Issuer": "DotNetJwtApi",
    "Audience": "JwtApiUsers",
    "DurationInMinutes": 60
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

πŸ” Step 8: Create TokenService

public class TokenService
{
    private readonly IConfiguration _config;

    public TokenService(IConfiguration config)
    {
        _config = config;
    }

    public string CreateToken(User user)
    {
        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, user.Email),
            new Claim("first_name", user.FirstName),
            new Claim("last_name", user.LastName),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]!));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            issuer: _config["Jwt:Issuer"],
            audience: _config["Jwt:Audience"],
            claims: claims,
            expires: DateTime.Now.AddMinutes(double.Parse(_config["Jwt:DurationInMinutes"]!)),
            signingCredentials: creds
        );

        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}

βš™οΈ Step 9: Configure Program.cs

builder.Services.AddControllers().AddNewtonsoftJson(options =>
{
    options.SerializerSettings.ContractResolver = new DefaultContractResolver
    {
        NamingStrategy = new SnakeCaseNamingStrategy()
    };
});

builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidateAudience = true,
            ValidAudience = builder.Configuration["Jwt:Audience"],
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!))
        };
    });

builder.Services.AddScoped<TokenService>();

var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();

🧾 Step 10: Create UsersController

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly ApplicationDbContext _context;
    private readonly TokenService _tokenService;

    public UsersController(ApplicationDbContext context, TokenService tokenService)
    {
        _context = context;
        _tokenService = tokenService;
    }

    [HttpPost]
    public async Task<IActionResult> Register([FromForm] UserDto userDto)
    {
        string? imageUrl = await SaveImage(userDto.Image);

        var user = new User
        {
            FirstName = userDto.FirstName,
            LastName = userDto.LastName,
            Email = userDto.Email,
            Password = userDto.Password,
            ImageUrl = imageUrl
        };

        _context.Users.Add(user);
        await _context.SaveChangesAsync();

        return Ok(user);
    }

    [HttpPost("login")]
    public async Task<IActionResult> Login([FromBody] LoginDto login)
    {
        var user = await _context.Users
            .FirstOrDefaultAsync(u => u.Email == login.Email && u.Password == login.Password);

        if (user == null)
            return Unauthorized(new { message = "Invalid credentials" });

        var token = _tokenService.CreateToken(user);

        return Ok(new
        {
            token,
            user = new { user.Id, user.FirstName, user.LastName, user.Email, user.ImageUrl }
        });
    }

    [Authorize]
    [HttpGet("dashboard")]
    public IActionResult Dashboard()
    {
        var name = User.FindFirst("first_name")?.Value;
        return Ok(new { message = $"Welcome back, {name}!" });
    }

    private async Task<string?> SaveImage(IFormFile? image)
    {
        if (image == null || image.Length == 0)
            return null;

        var folder = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads");
        if (!Directory.Exists(folder))
            Directory.CreateDirectory(folder);

        var fileName = Guid.NewGuid().ToString() + Path.GetExtension(image.FileName);
        var path = Path.Combine(folder, fileName);

        using (var stream = new FileStream(path, FileMode.Create))
        {
            await image.CopyToAsync(stream);
        }

        return "/uploads/" + fileName;
    }
}

πŸ“‘ Step 11: Run Migrations

dotnet ef migrations add InitialCreate
dotnet ef database update

πŸ§ͺ Step 12: Testing APIs with Postman

  • POST /api/users – Register (multipart/form-data)
  • POST /api/users/login – Login (JSON)
  • GET /api/users/dashboard – Protected route (send Bearer token)

βœ… Success! Your Secure API is Ready!

You’ve successfully built a secure .NET Core API with SQL Server, EF Core, JWT login & image upload support. You can now connect this backend to any Angular, React, or mobile frontend and control user authentication using JWT!

Leave a Reply

Your email address will not be published. Required fields are marked *