Reference · Fullstack · .NET 8 + Angular 16

JWT, authn/authz, CORS, dan error handling

Ringkasan satu halaman. Dari Lesson 0017.

Alur login (ucapkan dalam urutan ini)

  1. Form login → AuthService → POST /api/auth/login (HttpClient)
  2. Beda origin → browser kirim preflight OPTIONS → UseCors("angular")
  3. API cek kredensial → terbitkan JWT (header.payload.signature; claims sub, role, exp)
  4. Angular simpan token; AuthInterceptor: req.clone({ setHeaders: { Authorization: `Bearer ${t}` } })
  5. UseAuthentication: validasi signature, issuer, audience, expiry → HttpContext.User (ClaimsPrincipal)
  6. UseAuthorization: [Authorize] / [Authorize(Roles = "Admin")] / [Authorize(Policy = "...")]
  7. Exception → IExceptionHandler → ProblemDetails; interceptor: 401 → /login

Authentication vs authorization

AuthenticationAuthorization
PertanyaanSiapa kamu? (identitas)Boleh akses resource ini?
MiddlewareUseAuthentication (duluan)UseAuthorization (sesudahnya)
Gagal (JWT bearer)challenge → 401 + WWW-Authenticate: Bearerforbid → 403
Nodepassport-jwt → req.usermiddleware cek role

Authentication tidak otomatis mengunci endpoint: butuh [Authorize] atau fallback policy.

JWT di .NET 8

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddJwtBearer(o => o.TokenValidationParameters = new TokenValidationParameters {
      ValidateIssuer = true, ValidIssuer = jwt.Issuer,
      ValidateAudience = true, ValidAudience = jwt.Audience,
      ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(keyBytes),
      ValidateLifetime = true });
builder.Services.AddAuthorization();

CORS

builder.Services.AddCors(o => o.AddPolicy("angular", p =>
    p.WithOrigins("http://localhost:4200").AllowAnyHeader().AllowAnyMethod()));
app.UseRouting(); app.UseCors("angular"); app.UseAuthentication(); app.UseAuthorization();
Same-origin policyditegakkan browser; beda skema/host/port = beda origin; Postman/curl tidak terkena
CORSserver melonggarkan SOP lewat header Access-Control-*; bukan fitur keamanan
PreflightOPTIONS sebelum request bila ada header di luar safelist (mis. Authorization) atau Content-Type: application/json
Jebakanorigin tanpa / di akhir · jangan AllowAnyOrigin + AllowCredentials
Expressapp.use(cors({ origin: 'http://localhost:4200' }))

Global error handling

public class GlobalExceptionHandler : IExceptionHandler   // Singleton
{   ValueTask<bool> TryHandleAsync(HttpContext ctx, Exception ex, CancellationToken ct)
    // ValidationException → 400, NotFoundException → 404, lainnya → 500 (log, tanpa detail)
    // tulis ProblemDetails, return true = sudah ditangani
}
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
builder.Services.AddProblemDetails();
app.UseExceptionHandler();   // PALING AWAL (Express: error middleware paling AKHIR)

ProblemDetails (RFC 9457, dulu 7807): type, title, status, detail, instance. [ApiController] otomatis mengembalikan 400 ValidationProblemDetails saat model tidak valid. Angular: body ada di HttpErrorResponse.error.

Menyimpan JWT

localStorageCookie HttpOnly; Secure; SameSite
Dibaca JSyatidak
RisikoXSS mencuri tokenCSRF (cookie terkirim otomatis)
Mitigasicegah XSS, token pendekSameSite, anti-CSRF token (Angular: XSRF-TOKEN → X-XSRF-TOKEN, server wajib verifikasi)
Kirim lewatHTTP Interceptorbrowser (withCredentials lintas origin)

Microsoft: untuk web app yang aman, simpan token di server (BFF) dan hanya bagikan cookie HttpOnly. Jawab interview: trade-off + pilihan Anda + alasan.

Sumber