Β 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!