A Full Step-by-Step Tutorial for Beginners
Using Angular + ASP.NET Core Web API
Modern web applications require secure authentication. One of the most common and effective methods is JWT authentication. JWT stands for JSON Web Token, and it is widely used in single-page applications such as Angular, React, and Vue because it works very well with APIs.
In this tutorial, you will learn everything you need to build a full login system with JWT authentication using:
Angular (frontend)
ASP.NET Core Web API (backend)
JWT token generation and validation
SQL Server (optional but recommended)
This guide explains each step in simple language so even beginners can follow.
What You Will Build
By the end of this tutorial, you will have:
A login form in Angular
A .NET API endpoint that verifies users
A JWT token generated by the API
Angular storing the token in localStorage
Angular automatically attaching the token on protected requests
A protected API endpoint that only logged-in users can access
Token validation and token expiration handling
This is the foundation of any real-world authentication system.
What Is JWT
JWT means JSON Web Token.
It is a small, signed text string that stores user information.
Example JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
A JWT contains three parts:
Header
Payload (user data)
Signature (to verify token authenticity)
Once a user logs in:
JWT is stateless. The server does not store any session information.
Part 1: Backend – ASP.NET Core API with JWT
Step 1: Create a Web API Project
dotnet new webapi -n JwtAuthAPI
Step 2: Add JWT NuGet Package
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Step 3: Create User Model
In Models/User.cs:
public class User
{
public int Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
This is a simple example.
For real systems: hash passwords and use ASP.NET Identity.
Step 4: Configure JWT in appsettings.json
"Jwt": {
"Key": "ThisIsASecretKeyForJwtToken12345",
"Issuer": "JwtAuthAPI",
"Audience": "JwtAuthAPIUser"
}
The key must be long and secure.
Step 5: Configure JWT Authentication in Program.cs
Add:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
Then configure:
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)
};
});
Add middleware:
app.UseAuthentication();
app.UseAuthorization();
Step 6: Create Token Generator Service
Create Services/JwtService.cs:
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);
}
}
Register it in Program.cs:
builder.Services.AddScoped<JwtService>();
Step 7: Create Login Endpoint
In Controllers/AuthController.cs:
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
private readonly JwtService _jwt;
public AuthController(JwtService jwt)
{
_jwt = jwt;
}
[HttpPost("login")]
public IActionResult Login(User user)
{
// Dummy example, replace with database lookup
if (user.Email == "[email protected]" && user.Password == "123456")
{
var token = _jwt.GenerateToken(user.Email);
return Ok(new { Token = token });
}
return Unauthorized(new { Message = "Invalid credentials" });
}
}
Your backend is ready.
Step 8: Create Protected API Endpoint
[Authorize]
[HttpGet("profile")]
public IActionResult Profile()
{
return Ok(new { Message = "This is protected data" });
}
This endpoint requires a valid JWT.
Part 2: Frontend – Angular Authentication
Step 1: Create Angular Project
ng new jwt-auth-app
cd jwt-auth-app
Step 2: Enable HttpClientModule
In app.module.ts:
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [BrowserModule, HttpClientModule],
})
export class AppModule {}
Step 3: Create Login Form Component
ng generate component login
Step 4: Build Login Form
In login.component.html:
<h2>Login</h2>
<form (ngSubmit)="login()">
<label>Email</label>
<input type="email" [(ngModel)]="user.email" name="email" required>
<label>Password</label>
<input type="password" [(ngModel)]="user.password" name="password" required>
<button type="submit">Login</button>
</form>
<p>{{ message }}</p>
Step 5: Angular AuthService
ng generate service auth
In auth.service.ts:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private url = "https://localhost:5001/api/auth/login";
constructor(private http: HttpClient) {}
login(user: any) {
return this.http.post<any>(this.url, user);
}
saveToken(token: string) {
localStorage.setItem('token', token);
}
getToken() {
return localStorage.getItem('token');
}
isLoggedIn() {
return !!localStorage.getItem('token');
}
}
Step 6: Implement Login Method
In login.component.ts:
import { Component } from '@angular/core';
import { AuthService } from '../auth.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html'
})
export class LoginComponent {
message = "";
user = {
email: "",
password: ""
};
constructor(private authService: AuthService) {}
login() {
this.authService.login(this.user).subscribe({
next: (res) => {
this.authService.saveToken(res.token);
this.message = "Login successful";
},
error: () => {
this.message = "Invalid email or password";
}
});
}
}
Part 3: Protect API Calls From Angular
Step 1: Create Auth Interceptor
ng generate interceptor auth
In 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 it in app.module.ts:
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true
}
]
Now Angular automatically attaches JWT in requests.
Part 4: Call Protected API
In your Angular component:
getProfile() {
this.http.get("https://localhost:5001/api/auth/profile")
.subscribe(res => console.log(res));
}
If the user is logged in, this works.
If not, API returns 401 Unauthorized.
Part 5: Summary of System Flow
User enters email and password in Angular
Angular sends credentials to ASP.NET Core
API validates user
API returns JWT token
Angular saves token in localStorage
Angular adds token to Authorization header
API validates token on protected endpoints
If valid, data is returned
If invalid or expired, 401 Unauthorized is returned
This is the complete JWT authentication cycle.