ASP.NET Core  

Login + Register + JWT + Guards full project tutorial | Asp.Net Core

A Complete Full-Stack Project Tutorial
Using Angular + ASP.NET Core + SQL Server

This tutorial covers everything needed to build a real authentication system used in modern web applications.

What You Will Build

A full end-to-end system including:

  1. User Registration

  2. User Login

  3. JWT Authentication

  4. Password Hashing

  5. Protected API Endpoints

  6. Angular AuthService for token handling

  7. Angular Login + Register UI

  8. Route Guards to protect pages

  9. Auto-attach token to HTTP requests

  10. Redirect if user not authenticated

This is the foundation of almost every real web application.

Project Breakdown

We will structure this tutorial into 3 main parts:

  1. Backend: ASP.NET Core Web API

  2. Frontend: Angular Application

  3. Connecting the two

Let's begin.

PART 1: BACKEND (ASP.NET Core Web API)

Step 1: Create a new Web API project

dotnet new webapi -n AuthAPI
cd AuthAPI

Step 2: Install Required Packages

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package BCrypt.Net-Next

Step 3: Create SQL Server Database

CREATE DATABASE AuthDB;

Step 4: Create User Model

Create Models/User.cs:

public class User
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }
}

We store password hashes, not plain text.

Step 5: Add DbContext

File: Data/AppDbContext.cs

using Microsoft.EntityFrameworkCore;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    {
    }

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

Step 6: Configure SQL Server

In appsettings.json:

"ConnectionStrings": {
  "DefaultConnection": "Server=localhost;Database=AuthDB;Trusted_Connection=True;"
}

Step 7: Register DbContext

In Program.cs:

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

Step 8: Create JWT Service

File: Services/JwtService.cs

using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;
using System.Text;

public class JwtService
{
    private readonly IConfiguration _config;

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

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

        var claims = new[]
        {
            new Claim(ClaimTypes.Email, email)
        };

        var token = new JwtSecurityToken(
            issuer: _config["Jwt:Issuer"],
            audience: _config["Jwt:Audience"],
            claims: claims,
            expires: DateTime.Now.AddHours(1),
            signingCredentials: creds);

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

Step 9: Add JWT Config in appsettings.json

"Jwt": {
  "Key": "VerySecretKey123456789",
  "Issuer": "AuthAPI",
  "Audience": "AuthAPIUser"
}

Step 10: Configure JWT Middleware

In Program.cs:

var key = Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]);

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

builder.Services.AddScoped<JwtService>();

Middleware:

app.UseAuthentication();
app.UseAuthorization();

Step 11: Create Auth Controller

File: Controllers/AuthController.cs

Registration Endpoint

[HttpPost("register")]
public async Task<IActionResult> Register(UserDto request)
{
    if (_context.Users.Any(x => x.Email == request.Email))
        return BadRequest(new { Message = "Email already exists" });

    var user = new User
    {
        FullName = request.FullName,
        Email = request.Email,
        PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.Password)
    };

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

    return Ok(new { Message = "User Registered Successfully" });
}

Login Endpoint

[HttpPost("login")]
public IActionResult Login(LoginDto request)
{
    var user = _context.Users.FirstOrDefault(x => x.Email == request.Email);
    if (user == null)
        return Unauthorized(new { Message = "Invalid credentials" });

    if (!BCrypt.Net.BCrypt.Verify(request.Password, user.PasswordHash))
        return Unauthorized(new { Message = "Invalid credentials" });

    var token = _jwt.GenerateToken(user.Email);

    return Ok(new { Token = token });
}

Protected Endpoint

[Authorize]
[HttpGet("profile")]
public IActionResult Profile()
{
    return Ok(new { Message = "Access granted to protected data" });
}

Step 12: Create DTOs

File: Models/UserDto.cs

public class UserDto
{
    public string FullName { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
}

File: Models/LoginDto.cs

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

Step 13: Migrate Database

dotnet ef migrations add InitialCreate
dotnet ef database update

Backend is ready.

PART 2: FRONTEND (Angular)

Step 1: Create Angular Project

ng new auth-app
cd auth-app

Step 2: Install Required Packages

npm install jwt-decode

Step 3: Enable HttpClientModule

app.module.ts

import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [
    BrowserModule,
    HttpClientModule
  ]
})
export class AppModule { }

Step 4: Create AuthService

auth.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import jwtDecode from 'jwt-decode';

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  private apiUrl = "https://localhost:5001/api/auth";

  constructor(private http: HttpClient) {}

  register(data: any) {
    return this.http.post(`${this.apiUrl}/register`, data);
  }

  login(data: any) {
    return this.http.post<any>(`${this.apiUrl}/login`, data);
  }

  saveToken(token: string) {
    localStorage.setItem('token', token);
  }

  getToken() {
    return localStorage.getItem('token');
  }

  isLoggedIn(): boolean {
    const token = this.getToken();
    if (!token) return false;

    const decoded: any = jwtDecode(token);
    return Date.now() < decoded.exp * 1000;
  }

  logout() {
    localStorage.removeItem('token');
  }
}

Step 5: Auth Interceptor

Attach token automatically.

ng generate interceptor auth

auth.interceptor.ts

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http';
import { AuthService } from './auth.service';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor(private auth: AuthService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler) {
    const token = this.auth.getToken();

    if (token) {
      req = req.clone({
        setHeaders: { Authorization: `Bearer ${token}` }
      });
    }

    return next.handle(req);
  }
}

Enable in app.module.ts:

{
  provide: HTTP_INTERCEPTORS,
  useClass: AuthInterceptor,
  multi: true
}

Step 6: Create Register Component

ng generate component register

register.component.html

<h2>Register</h2>

<form (ngSubmit)="submit()">

  <input type="text" [(ngModel)]="model.fullName" name="fullName" placeholder="Full Name">

  <input type="email" [(ngModel)]="model.email" name="email" placeholder="Email">

  <input type="password" [(ngModel)]="model.password" name="password" placeholder="Password">

  <button>Register</button>

</form>

<p>{{ message }}</p>

register.component.ts

import { Component } from '@angular/core';
import { AuthService } from '../auth.service';

@Component({
  selector: 'app-register',
  templateUrl: './register.component.html'
})
export class RegisterComponent {

  model = { fullName: '', email: '', password: '' };
  message = '';

  constructor(private auth: AuthService) {}

  submit() {
    this.auth.register(this.model).subscribe({
      next: (res: any) => this.message = res.message,
      error: err => this.message = err.error.message
    });
  }
}

Step 7: Create Login Component

ng generate component login

login.component.html

<h2>Login</h2>

<form (ngSubmit)="submit()">

  <input type="email" [(ngModel)]="model.email" name="email" placeholder="Email">

  <input type="password" [(ngModel)]="model.password" name="password" placeholder="Password">

  <button>Login</button>

</form>

<p>{{ message }}</p>

login.component.ts

import { Component } from '@angular/core';
import { AuthService } from '../auth.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html'
})
export class LoginComponent {

  model = { email: '', password: '' };
  message = '';

  constructor(private auth: AuthService) {}

  submit() {
    this.auth.login(this.model).subscribe({
      next: (res) => {
        this.auth.saveToken(res.token);
        this.message = "Login successful";
      },
      error: err => this.message = "Invalid credentials"
    });
  }
}

Step 8: Create Dashboard Component (Protected)

ng generate component dashboard

dashboard.component.ts

import { HttpClient } from '@angular/common/http';
import { Component } from '@angular/core';

@Component({
  templateUrl: './dashboard.component.html'
})
export class DashboardComponent {

  message = "";

  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.http.get("https://localhost:5001/api/auth/profile")
    .subscribe(res => this.message = "Protected Data Loaded");
  }
}

Step 9: Auth Guard

ng generate guard auth

auth.guard.ts

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {

  constructor(private auth: AuthService, private router: Router) {}

  canActivate() {
    if (this.auth.isLoggedIn()) return true;
    this.router.navigate(['/login']);
    return false;
  }
}

Step 10: Setup Routing

app-routing.module.ts

const routes: Routes = [
  { path: 'register', component: RegisterComponent },
  { path: 'login', component: LoginComponent },
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivate: [AuthGuard]
  },
  { path: '', redirectTo: '/login', pathMatch: 'full' }
];

PART 3: RUN & TEST

Step 1: Run backend

dotnet run

Step 2: Run Angular

ng serve

Test the Flow

  1. Go to /register

  2. Register a new user

  3. Go to /login

  4. Login with your new account

  5. Token stored in localStorage

  6. Visit /dashboard

  7. Only allowed if logged in

  8. Try deleting token manually → auto redirects to login

Everything works end-to-end.

Conclusion

You now have a fully working authentication system with:

  • User Registration

  • Login

  • JWT Authentication

  • Token Storage

  • Protected API routes

  • Angular Guards

  • Auto token attachment with interceptors

  • SQL Server database

  • Password hashing

This is a real-world, production-ready foundation.