From c16110d0b57a37ad6aeb986ef22848b1ca2275c0 Mon Sep 17 00:00:00 2001 From: Mehrdad Shirvani Date: Mon, 2 Jun 2025 10:35:28 +0330 Subject: [PATCH] vault backup: 2025-06-02 10:35:28 --- .../Session07/Session07 Additional Info.md | 785 ++++++++++++++++++ .../Session07/Session07 Backend.md | 527 ++++++++++++ .../Session07/Session07 Frontend.md | 304 +++++++ .../Session07/Session07.md | 82 -- 4 files changed, 1616 insertions(+), 82 deletions(-) create mode 100644 02_ProjectOrientedSessions/Session07/Session07 Additional Info.md create mode 100644 02_ProjectOrientedSessions/Session07/Session07 Backend.md create mode 100644 02_ProjectOrientedSessions/Session07/Session07 Frontend.md delete mode 100644 02_ProjectOrientedSessions/Session07/Session07.md diff --git a/02_ProjectOrientedSessions/Session07/Session07 Additional Info.md b/02_ProjectOrientedSessions/Session07/Session07 Additional Info.md new file mode 100644 index 0000000..384dee3 --- /dev/null +++ b/02_ProjectOrientedSessions/Session07/Session07 Additional Info.md @@ -0,0 +1,785 @@ + +# AUTHENTICATION METHODS FOR ASP.NET CORE WEB API + +### 1. **JWT (JSON Web Token) Authentication** + +**Most common for SPAs (like your React frontend)** + +- βœ… **Stateless**: No session stored on the server. + +- πŸ” **Token structure**: Header + Payload (including roles) + Signature. + +- 🎯 Good for: SPAs, mobile apps, APIs. + +- πŸ“¦ Libraries: `Microsoft.AspNetCore.Authentication.JwtBearer`. + + +**Setup Highlights:** + +- Client sends credentials β†’ API generates token with user roles β†’ Client stores token β†’ Client sends token in `Authorization` header (`Bearer `). + +- Server verifies signature and extracts user identity and roles from the token. + + +**Role support**: + +- You embed roles inside the JWT (`"roles": ["Admin", "User"]`). + +- Use `[Authorize(Roles = "Admin")]`. + + +--- + +### 2. **Cookie-Based Authentication** + +**Traditional approach for server-rendered apps (not ideal for APIs)** + +- πŸ›‘ **Not recommended for APIs** due to CSRF vulnerability and session overhead. + +- πŸ—‚ Stores session identifier in browser cookie. + +- Useful when using ASP.NET Core MVC or Razor Pages (not Web API). + + +--- + +### 3. **OAuth2 + OpenID Connect (OIDC)** + +**Best for federated login, single sign-on (SSO), or external providers** + +- πŸ”— Integrates with Identity Providers (IDPs) like: + + - Azure AD + + - Google, Facebook, GitHub + + - Auth0, Okta, Duende IdentityServer + +- πŸ“¦ Library: `Microsoft.AspNetCore.Authentication.OpenIdConnect` + +- Uses **access tokens** (JWT) issued by an authority. + + +**Role support**: + +- Roles/claims provided by the Identity Provider. + +- You map these claims to roles in the API. + +- `[Authorize(Roles = "Admin")]` still works. + + +--- + +### 4. **API Key Authentication** + +**Lightweight alternative (not ideal for user-based roles)** + +- Client includes a static API key in headers or query string. + +- πŸ” No user context β†’ ❌ no role-based support unless you map API keys to roles in a custom way. + +- πŸ”§ Implemented manually in middleware or filters. + + +--- + +### 5. **Basic Authentication** + +- User provides `username:password` in Base64 via `Authorization` header. + +- ❌ Insecure unless used with HTTPS. + +- ⚠️ Rarely used anymore β€” not good for role-based systems or production apps. + + +--- + +### 6. **ASP.NET Core Identity** + +**Full-featured user management system (often combined with JWT)** + +- βœ… Provides login, registration, role management, password hashing, etc. + +- 🎯 Good choice if you want to **own the user system** and **manage roles** yourself. + +- Can be used with: + + - JWT tokens (custom token generation) + + - Cookie auth (not for APIs) + +- πŸ”§ Use `UserManager`, `RoleManager`. + + +**Example**: Use Identity for creating users and roles, then issue JWTs on login. + +--- + + +## Summary Table + +|Method|Stateless|Token-Based|Role Support|Ideal For| +|---|---|---|---|---| +|JWT Authentication|βœ…|βœ…|βœ…|APIs, SPAs (React, etc.)| +|Cookie Authentication|❌|❌|βœ…|Server-side apps only| +|OAuth2 + OpenID Connect|βœ…|βœ…|βœ…|External login, SSO, enterprise| +|API Key|βœ…|❌|❌ (manual)|Simple apps, service-to-service| +|Basic Auth|βœ…|❌|❌ (manual)|Very basic use, not recommended| +|ASP.NET Core Identity|❌|Optional|βœ…|User/Role management| + +--- + +# Web Security and JWT Terms + + +### 🧨 **CSP (Content Security Policy)** + +**Definition:** +A **browser security mechanism** that helps prevent **XSS attacks** by controlling which sources the browser can load content from. + +**Example Usage:** +It can prevent JavaScript from running unless it's from a trusted source. + +**Example CSP header:** + +```http +Content-Security-Policy: default-src 'self'; script-src 'self' https://trustedscripts.example.com +``` + +--- + +### πŸ’₯ **XSS (Cross-Site Scripting)** + +**Definition:** +A vulnerability where an attacker **injects malicious JavaScript** into a web page that gets executed in a user’s browser. + +**Why It Matters for JWT:** +If you store JWTs in `localStorage`, and your app is vulnerable to XSS, the attacker can steal the token and impersonate the user. + +**Prevention:** + +- Never trust user input (sanitize!) + +- Use **CSP headers** + +- Avoid inline scripts + +- Use frameworks that auto-escape HTML (like React, Razor) + + +--- + +### πŸ“¦ **Payload (in JWT)** + +**Definition:** +The **middle part** of a JWT. It contains the **claims** (user data, permissions, roles, etc.) in a **Base64-encoded JSON** format. + +**Example:** + +```json +{ + "sub": "1234567890", + "name": "John Doe", + "role": "Admin", + "exp": 1716850984 +} +``` + +⚠️ **Note:** Payload is **not encrypted**, just encoded β€” anyone can read it, but not modify it without invalidating the signature. + +--- + +### ✍️ **Signature (in JWT)** + +**Definition:** +The **third part** of the token. It's a cryptographic hash (HMAC or RSA/ECDSA) of the header and payload, signed with a **secret** or private key. + +**Purpose:** +To verify that the token **hasn’t been tampered with**. + +**Structure:** + +``` +JWT = base64(header) + '.' + base64(payload) + '.' + signature +``` + +Only the server (with the secret key) can validate the signature. + +--- + +### 🦠 **CSRF (Cross-Site Request Forgery)** + +**Definition:** +A vulnerability where an attacker tricks a user’s **browser** (with a valid session/cookie) into making an unwanted request to your site **without the user’s knowledge**. + +**Example:** +If you're logged in and visit a malicious site, that site may submit a POST request using your cookies to perform actions on your behalf. + +**Why It Matters:** + +- If you store JWTs in **HttpOnly cookies**, you must guard against CSRF. + + +**Defense:** + +- Use `SameSite=Strict` cookies + +- Use anti-CSRF tokens + +- Prefer `Authorization` header with tokens (doesn’t auto-send like cookies) + + +--- + +### 🧠 **Session Overhead** + +**Definition:** +The **memory and server resource cost** of maintaining a session for each logged-in user on the server. + +**Why It Matters:** + +- Traditional **cookie-based auth** stores session info on the server. + +- With JWT, the session is **stateless** (no server memory), reducing overhead and scaling better. + + +--- + +## πŸ”‘ **Authentication Ecosystem Concepts** + +--- + +### 🌐 **Federated Login** + +**Definition:** +Users log in to your application using **another trusted identity provider** (IdP), such as: + +- Google + +- Facebook + +- Microsoft + +- GitHub + + +You delegate authentication to the third party and just receive user info (often as a JWT or OpenID Connect token). + +**Protocol Examples:** + +- OAuth2 + +- OpenID Connect + + +--- + +### πŸ” **SSO (Single Sign-On)** + +**Definition:** +A user logs in **once** and gains access to **multiple systems or applications** without logging in again. + +**Common in Enterprises**: + +- Logging into one dashboard gives you access to HR system, email, file storage, etc. + + +**How It Works:** + +- A **central identity provider (IdP)** issues tokens + +- Applications **trust the token** and skip login + +- Often uses **OAuth2**, **OpenID Connect**, or **SAML** + + +--- + +## πŸ”„ Summary Table + +|Term|Meaning| +|---|---| +|**CSP**|Restricts sources of scripts/styles to prevent XSS| +|**XSS**|Injected JavaScript that steals data like JWT tokens| +|**JWT Payload**|JSON with user claims (readable but not secure on its own)| +|**JWT Signature**|Proves the token is untampered (signed by server)| +|**CSRF**|Unauthorized actions using authenticated user's browser/session| +|**Session Overhead**|Cost of storing user sessions in memory on the server| +|**Federated Login**|Login using a third-party identity provider (e.g., Google)| +|**SSO**|One login grants access to multiple trusted systems| + + +# ASP.NET Identity vs JWT Authentication + +--- + +### βœ… **What is ASP.NET Identity?** + +**ASP.NET Identity** is a full **membership system** that: + +- Manages users, roles, passwords, claims, tokens, and external logins. + +- Stores user data in a **database** (usually using Entity Framework). + +- Works well with **cookie-based authentication** by default. + +- Handles login, logout, registration, password hashing, email confirmation, two-factor auth, etc. + + +**Default storage:** SQL Server (via Entity Framework) + +--- + +### πŸ”‘ **How Authentication Works in ASP.NET Identity (Cookie-Based)** + +1. **Login request**: The user submits a form with username/password. + +2. **Server validates** the credentials using ASP.NET Identity. + +3. If valid, the server issues a **cookie** (with a session token). + +4. The browser stores this **authentication cookie**. + +5. For future requests, the browser **automatically sends the cookie**. + +6. The server validates the cookie (and reads the user session from memory or database). + + +βœ… **Stateful** +❌ Doesn't scale well for large APIs unless you add external session storage (like Redis). + +--- + +### πŸ” What is JWT Authentication? + +JWT Authentication is: + +- **Stateless**. + +- Based on tokens β€” not sessions. + +- Works well for APIs and SPAs/mobile apps. + + +#### Flow: + +1. **User logs in**, and if credentials are valid... + +2. Server **creates a JWT** containing user info (e.g., roles). + +3. Server **signs the token** and sends it to the client. + +4. Client stores it (e.g., in localStorage or cookies). + +5. On every request, the client sends the JWT in the `Authorization` header. + +6. Server **verifies the JWT signature** using a secret or key. + +7. If valid β†’ allow access (no server-side session needed). + + +βœ… **Stateless** +βœ… Scales easily +βœ… Good for distributed APIs + +--- + +### πŸ“Š Comparison Table + +|Feature|ASP.NET Identity (Cookie)|JWT Authentication (Token-Based)| +|---|---|---| +|**Stateful/Stateless**|Stateful|Stateless| +|**Storage**|Cookie on client, session on server|Token on client only| +|**Default Transport**|Cookie (auto-sent by browser)|Authorization header (manual send)| +|**Built-in Support**|ASP.NET Identity (UI + EF Core)|ASP.NET Core + Manual JWT setup| +|**Scalability**|Limited (server stores session)|High (no session to manage)| +|**Security**|Cookie CSRF risk|XSS risk if stored in JS-accessible storage| +|**Use Case**|Web apps with UI (MVC, Razor)|APIs, SPAs, mobile apps| +|**External login support**|Built-in|Needs integration| +|**Token expiration**|Server-controlled session|Token has expiration embedded| + +--- + +### 🚨 Statelessness β€” What Does It Mean? + +- **Stateful Authentication**: Server **stores a session** (usually in memory or a database) for each user. The client just stores a cookie with a session ID. + + - When user logs in, server keeps a record of that. + + - Logout β†’ delete session. + +- **Stateless Authentication** (JWT): + + - Server **does not remember anything**. + + - JWT has all info about the user in itself (claims, roles, expiry). + + - Logout = just delete token on client side (server has no "memory"). + + +⚠️ **You can't "force logout" someone server-side in pure JWT unless you blacklist tokens manually** (or rotate secrets). + +--- + +### πŸ’‘ Can You Use ASP.NET Identity with JWT? + +Yes β€” ASP.NET Identity can be configured to: + +- Authenticate user credentials + +- Then issue a **JWT** instead of using a cookie + +- This way, you get: + + - ASP.NET Identity’s user management + + - JWT’s **stateless** API authentication + + +This is often used in **hybrid apps**: + +- Use Identity for registration/login + +- Use JWT for frontend and mobile API calls + + + +# What does `null!` mean? + +It's a **null-forgiving operator**. + +When you write: + +```csharp +string? maybeNull = null; +string definitelyNotNull = null!; // ← This tells the compiler: β€œTrust me, it’s not null.” +``` + +You're telling the compiler: + +> _β€œI know this is technically null, but treat it as non-null and don’t warn me.”_ + +--- + +### 🧩 Why is this needed? + +C# 8 introduced **nullable reference types**, where the compiler gives warnings if you assign or use `null` where it's not safe. + +For example: + +```csharp +string name = null; // Warning: assigning null to non-nullable reference +``` + +To silence the warning, you can use `null!`: + +```csharp +string name = null!; // No warning +``` + +But this is **your responsibility**: the compiler **assumes you're guaranteeing** that it won’t cause a `NullReferenceException`. + +--- + +### ⚠️ When to use `null!`? + +Use it **sparingly and carefully**, only when: + +- You know an object **will be initialized later**. + +- You’re working with **ORMs like EF Core** where properties are set **after construction**. + +- You're dealing with **dependency injection** and the value is assigned **automatically later**. + + +#### Example: + +```csharp +public class Account +{ + public string PhoneNumber { get; set; } = null!; // EF will populate it +} +``` + +--- + +### βœ… Summary + +|Expression|Meaning| +|---|---| +|`null!`|"This is null, but treat it as if it's non-null (trust me)."| +|Use case|Avoiding compiler warnings for nullable reference types| +|Risk|Can lead to runtime `NullReferenceException` if used incorrectly| + +--- + +Let me know if you want help turning on/off nullable reference types or dealing with EF Core model warnings. + + +# What is a JWT Generator? + +A **JWT Generator** is a class or service in your backend that: + +1. **Generates** a signed JWT (JSON Web Token) when a user logs in. + +2. **Encodes** the user's identity, roles, and other claims. + +3. **Signs** the token with a secret or private key so it can be validated later. + + +--- + +## πŸ”§ Structure of a JWT + +A JWT has 3 parts: + +```plaintext +xxxxx.yyyyy.zzzzz +``` + +1. **Header** (Base64-encoded JSON): + + ```json + { + "alg": "HS256", + "typ": "JWT" + } + ``` + +2. **Payload** (Base64-encoded JSON): + Contains user data and claims (e.g., user ID, role, expiry). + + ```json + { + "sub": "userId123", + "phone": "0930xxx", + "role": "Admin", + "exp": 1717502800 + } + ``` + +3. **Signature**: + HMACSHA256(header + "." + payload, secret key) + + +--- + +## βœ… What Goes into the Payload? + +Include things you want to check _without querying the DB every time_: + +- `sub` (Subject – usually user ID) + +- `phone` or username + +- `role` (e.g., "Admin", "User") + +- `exp` (expiration timestamp) + +- Any custom claim, like `companyId`, `verified`, etc. + + +--- + +## πŸ” Signature + +The **signature ensures** that the token hasn't been tampered with. If the signature doesn't match (due to modification or incorrect secret), the token is invalid. + +--- + +## 🧱 Example: JWT Generator in ASP.NET Core + +```csharp +public interface IJwtGenerator +{ + string GenerateToken(Account account); +} +``` + +```csharp +public class JwtGenerator : IJwtGenerator +{ + private readonly IConfiguration _config; + + public JwtGenerator(IConfiguration config) + { + _config = config; + } + + public string GenerateToken(Account account) + { + var claims = new[] + { + new Claim(JwtRegisteredClaimNames.Sub, account.Id.ToString()), + new Claim(JwtRegisteredClaimNames.PhoneNumber, account.PhoneNumber), + new Claim(ClaimTypes.Role, account.Role), + }; + + 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.UtcNow.AddHours(2), + signingCredentials: creds + ); + + return new JwtSecurityTokenHandler().WriteToken(token); + } +} +``` + +--- + +## πŸ›  Configuration in `appsettings.json` + +```json +"Jwt": { + "Key": "YourSuperSecureSecretKey123!", + "Issuer": "YourAppName", + "Audience": "YourFrontendApp" +} +``` + +--- + +## πŸ§ͺ Token Validation + +When the frontend sends this token on requests (via `Authorization: Bearer ` header), ASP.NET Core automatically validates: + +- Signature (using the same secret) + +- Expiry (`exp`) + +- Audience, Issuer + +- Claims (like role) + + +This is done via `JwtBearer` middleware. + +--- + +## πŸ” Roles in the Token? + +Yes, you can (and should) include user roles. The backend will **automatically enforce** `[Authorize(Roles = "Admin")]` using that claim. + +But: + +- Don't rely **only** on frontend logic β€” always **protect endpoints** on the backend too. + +- Frontend can use roles to **hide UI elements**, but not for enforcing access. + + +--- + +## πŸ€” Should You Encrypt the Token? + +No, standard practice is: + +- Don't encrypt JWTs β€” they’re just base64-encoded. + +- **Don’t put sensitive data** inside. + +- Sign them to prevent tampering. + +- Secure the token in frontend (e.g., HttpOnly cookies or `localStorage` with care). + + +--- + +## βœ… Summary of Responsibilities + +|Responsibility|Backend (JWT Generator)|Frontend| +|---|---|---| +|Token generation|βœ…|❌| +|Storing token|❌|βœ… (localStorage / cookie)| +|Sending token|❌|βœ… (Authorization header)| +|Token validation|βœ… (`JwtBearer`)|❌| +|Role enforcement|βœ… (`[Authorize]`)|βœ… (UI-level only)| + + + +--- + +# How multiple roles in JWT claims work + +- The JWT claims are basically a list of key-value pairs. + +- For roles, the key is usually `"role"` or `ClaimTypes.Role`. + +- You add **multiple claims with the same key** β€” one for each role. + + +**Example:** + +```json +{ + "sub": "1234567890", + "phone_number": "123456789", + "role": "Admin", + "role": "Editor", + "role": "User", + "exp": 1711600000 +} +``` + +- When the token is created, it contains multiple `"role"` entries. + +- On the backend, ASP.NET Core `ClaimsPrincipal` reads all of them, so `[Authorize(Roles="Admin,Editor")]` works by checking if **any** of the roles match. + + +--- + +### How frontend handles multiple roles in the JWT + +1. The frontend receives the JWT (usually after login). + +2. It **decodes** the JWT payload (using a library like `jwt-decode`). + +3. It extracts the roles as an **array of strings**. + + +Example in React using `jwt-decode`: + +```js +import jwtDecode from 'jwt-decode'; + +const token = localStorage.getItem('token'); +const decoded = jwtDecode(token); +const roles = decoded.role; // roles is usually an array if multiple roles exist + +console.log(roles); // ["Admin", "Editor", "User"] +``` + +4. The frontend can then use these roles to: + + - Conditionally render UI components or routes. + + - Show/hide buttons, pages, or features. + + +--- + +### Important security note for frontend roles + +- The frontend can **only do UI-level checks** based on roles. + +- **Never trust frontend role checks to secure data or APIs.** + +- The backend must always verify the token and roles via `[Authorize]` attributes or middleware. + + +--- + +### Summary + +|Step|How it works| +|---|---| +|JWT creation|Multiple `"role"` claims added to token| +|Backend authorization|Validates token and checks if user has required roles| +|Frontend decoding|Extracts `role` claim(s) as array of strings| +|Frontend UI control|Shows or hides content based on roles| diff --git a/02_ProjectOrientedSessions/Session07/Session07 Backend.md b/02_ProjectOrientedSessions/Session07/Session07 Backend.md new file mode 100644 index 0000000..ecccd94 --- /dev/null +++ b/02_ProjectOrientedSessions/Session07/Session07 Backend.md @@ -0,0 +1,527 @@ + +# Add a field in Database +- [ ] Add `User` field in `Roles` table + +# Branching +Β - [ ] Create the feature/authentication branch based on develop + +# Adjusting Account and Configurations + +- [ ] Add navigation property for `AccountRoles` in Account +```c# +public virtual ICollection AccountRoles { get; set; } +``` + +- [ ] Add navigation properties for Account and Role in `AccountRole` +```C# +public virtual Role Role { get; set; } +public virtual Account Account{ get; set; } +``` + +- [ ] Update the entity configuration to reflect relationship mappings: +```c# +builder.HasOne(ar => ar.Account) + .WithMany(a => a.AccountRoles) + .HasForeignKey(ar => ar.AccountId) + .OnDelete(DeleteBehavior.Restrict); + +builder.HasOne(ar => ar.Role) + .WithMany() + .HasForeignKey(ar => ar.RoleId) + .OnDelete(DeleteBehavior.Restrict); +``` + +# Creating DTOs +πŸ“‚ Suggested Folder: ApplicationLayer/DTOs/[RelatedFolder] +## AccountDto +- [ ] Create `AccountDto` to expose relevant account information: +```c# +public class AccountDto +{ + public long Id { get; set; } + public required string PhoneNumber { get; set; } + public required string Password { set; get; } + public string? Email { get; set; } + public long? PersonId { get; set; } + public List Roles { get; set; } +} +``` +## AuthResponseDto +- [ ] Define a DTO for authentication responses: +```c# +public class AuthResponseDto +{ + public long Id { get; set; } + public string Token { get; set; } = null!; + public string PhoneNumber { get; set; } = null!; + public List Roles { get; set; } +} +``` +- [ ] Define a DTO for login requests: +```c# +public class LoginRequestDto +{ + public string PhoneNumber { get; set; } = null!; + public string Password { get; set; } = null!; +} +``` +## RegisterRequestDto +- [ ] Define a DTO for registration with validation attributes: +```c# +public class RegisterRequestDto +{ + [Required(ErrorMessage = "Phone number is required.")] + [Phone(ErrorMessage = "Phone number format is invalid.")] + public required string PhoneNumber { get; set; } + + [Required(ErrorMessage = "Password is required.")] + [MinLength(6, ErrorMessage = "Password must be at least 6 characters long.")] + public required string Password { get; set; } + + [Compare("Password", ErrorMessage = "Passwords do not match.")] + public required string ConfirmPassword { get; set; } +} +``` + + +### πŸ”Ή **1. What do the annotations like `[Required]`, `[Phone]`, `[MinLength]`, `[Compare]` on the DTO do?** + +These are **Data Annotations** from `System.ComponentModel.DataAnnotations`. + +They’re used by: + +- The ASP.NET Core `[ApiController]` attribute + +- **Model binding & automatic validation** + + +**What happens:** +If your controller is marked with `[ApiController]`, ASP.NET Core will **automatically validate** the DTO against these annotations **before entering your action method**. + +Example: + +```csharp +[ApiController] +public class AuthController : ControllerBase +``` + +Then this: + +```csharp +[HttpPost("register")] +public async Task Register(RegisterRequestDto dto) +``` + +If `dto.PhoneNumber` is missing, it **won’t even run your logic**, and will return a `400 Bad Request` with validation errors. + + +> β€œWhy are these here if my frontend is separate?” + +βœ… **Answer**: They're still useful: + +- For **security and safety**: you _must_ validate on the backend β€” never trust the frontend. + +- For **auto validation** before hitting your logic β€” saving you boilerplate checks. + +- You can use them for **Swagger/OpenAPI documentation** as well. + + +Frontend validation is for **user experience**, not security. + +--- + +### πŸ”Ή **2. Where should password requirements be checked? Frontend or backend?** + +βœ… **Both.** + +- **Frontend**: show real-time UX feedback (β€œPassword must be 6+ characters”). + +- **Backend**: enforce security. + + +**Backend is the source of truth.** +Frontend can be bypassed (e.g., Postman). + +In the backend, you can either: + +- Use annotations like `[MinLength(6)]` + +- Or do manual checks: + + +```csharp +if (dto.Password.Length < 6) + return BadRequest("Password must be at least 6 characters long."); +``` + +--- + +### πŸ”Ή **3. Should confirm password be in the backend?** + +βœ… **Yes β€” if you're doing password comparison in backend.** + +- `[Compare("Password")]` will validate if `ConfirmPassword` matches. + +- Otherwise, you’ll need to check manually. + + +You **can skip sending ConfirmPassword to backend** and just validate in frontend if you’re confident your frontend handles it. + +But again: if someone sends malformed input manually (e.g., via Postman), backend should defend. + +πŸ’‘ **Best practice:** + +- Validate `ConfirmPassword` in frontend (UX) + +- Do one last check in backend, or use `[Compare]` for auto-validation + + +--- + +### πŸ”Ή **4. Is it OK to send plain password in request? Or should we hash it on frontend?** + +**βœ… YES β€” it is OK and standard to send raw password in the login/signup request.** + +Why? + +- Passwords are sent over **HTTPS**, which encrypts the entire request. + +- Hashing on frontend is _not_ secure, because: + + - Your algorithm/salt would be exposed + + - It defeats the purpose of salting and hashing correctly + + - You lose control over security management + + +### πŸ”Ή **5. Error Response from Automatic Model Validation** + +If your DTO looks like this: + +```csharp +public class RegisterRequestDto +{ + [Required(ErrorMessage = "Phone number is required.")] + [Phone(ErrorMessage = "Phone number format is invalid.")] + public string PhoneNumber { get; set; } + + [Required(ErrorMessage = "Password is required.")] + [MinLength(6, ErrorMessage = "Password must be at least 6 characters long.")] + public string Password { get; set; } + + [Compare("Password", ErrorMessage = "Passwords do not match.")] + public string ConfirmPassword { get; set; } +} +``` + +And the frontend sends this: + +```json +{ + "phoneNumber": "", + "password": "123", + "confirmPassword": "abc" +} +``` + +#### The backend will automatically return: + +```json +{ + "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", + "title": "One or more validation errors occurred.", + "status": 400, + "errors": { + "PhoneNumber": [ + "Phone number is required." + ], + "Password": [ + "Password must be at least 6 characters long." + ], + "ConfirmPassword": [ + "Passwords do not match." + ] + } +} +``` + +This is thanks to `[ApiController]` on your controller class. The framework uses the **ModelState** and returns errors in a structured way. + +--- + + + +## Adding Mappings +- [ ] Update `MappingProfile` with the following mappings: + +```c# +CreateMap() + .ForMember(dest => dest.Roles, opt => opt.MapFrom(src => src.AccountRoles.Select(x=>x.Role.Title))); +CreateMap() +.ForMember(dest => dest.AccountRoles, opt => opt.Ignore()); +``` + +# Add Password Hasher Utility +- [ ] Create a password hashing utility class +πŸ“‚ Suggested Folder: ApplicationLayer/Utils/`PasswordHasher.cs` +```c# +public static class PasswordHasher +{ + public static string HashPassword(string password) + { + byte[] salt = RandomNumberGenerator.GetBytes(16); + + var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100000, HashAlgorithmName.SHA256); + byte[] hash = pbkdf2.GetBytes(32); + + byte[] hashBytes = new byte[48]; + Array.Copy(salt, 0, hashBytes, 0, 16); + Array.Copy(hash, 0, hashBytes, 16, 32); + + return Convert.ToBase64String(hashBytes); + } + + public static bool VerifyPassword(string password, string hashedPassword) + { + byte[] hashBytes = Convert.FromBase64String(hashedPassword); + + byte[] salt = new byte[16]; + Array.Copy(hashBytes, 0, salt, 0, 16); + + var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100000, HashAlgorithmName.SHA256); + byte[] hash = pbkdf2.GetBytes(32); + + for (int i = 0; i < 32; i++) + { + if (hashBytes[i + 16] != hash[i]) + return false; + } + + return true; + } +} +``` + + +# Modifying Account Repository + +- [ ] Add the following methods in **`IAccountRepository`** + +```c# +Task GetByPhoneNumberAsync(string phoneNumber); +Task AddAccountRoleAsync(AccountRole accountRole); +``` + +- [ ] Implement the methods in **`AccountRepository`** +```c# + public async Task AddAccountRoleAsync(AccountRole accountRole) + { + await DbContext.AccountRoles.AddAsync(accountRole); + } + + public async Task GetByPhoneNumberAsync(string phoneNumber) + { + var user = await DbContext.Accounts.Include(x => x.AccountRoles).ThenInclude(x => x.Role).FirstOrDefaultAsync(x => x.PhoneNumber == phoneNumber); + return user; + } +``` + +# Creating Service +## Fix `Result.cs` Error Method +- [ ] Update the `Error` method to include error messages: +```c# +public static Result Error(T data, string errorMessage) => new() { Status = ResultStatus.Error, Data = data, ErrorMessage = errorMessage }; +``` + +## Creating `IAuthService.cs` and `AuthService.cs` +- [ ] Define the `IAuthService` interface +```c# +public interface IAuthService +{ + Task> RegisterAsync(RegisterRequestDto request); + Task> LoginAsync(LoginRequestDto request); +} +``` + +- [ ] Implement the interface in `AuthService.cs` + +Use this project as a reference: +https://github.com/MehrdadShirvani/AlibabaClone-Backend/blob/develop/AlibabaClone.Application/Services/AuthService.cs + +## Register `IAuthService` in Service in `Program.cs` + +- [ ] Add to `Program.cs` +```c# +//... +builder.Services.AddScoped(); +//... +``` + + +# Adding JWT + +## Installing Required NuGet Packages +Install these packages in the **`WebApi` (Presentation Layer)** project: +``` +Microsoft.AspNetCore.Authentication.JwtBearer +Microsoft.IdentityModel.Tokens +System.IdentityModel.Tokens.Jwt +``` + +## Create JWT Configuration Classes + +- [ ] Add JWT section to `appsettings.json` +```xaml +"Jwt": { + "Key": "[supersecretkeyyoustoresecurely]", + "Issuer": "[Issuer]", + "Audience": "MyAppUsers", + "ExpiryMinutes": 60 +} +``` + +- [ ] Create `JwtSettings` and add the following method +πŸ“‚ Suggested Folder: WebAPI/Authentication +```c# +public class JwtSettings +{ + public string Key { get; set; } = null!; + public string Issuer { get; set; } = null!; + public string Audience { get; set; } = null!; + public int ExpiryMinutes { get; set; } +} +``` + +- [ ] Create `IJwtGenerator` and add the following method +πŸ“‚ Suggested Folder: WebAPI/Authentication +```c# +string GenerateToken(AuthResponseDto authResponseDto); +``` + + +- [ ] Create `JwtGenerator`, implementing `IJwtGenerator` +πŸ“‚ Suggested Folder: WebAPI/Authentication + +use this project as a reference +https://github.com/MehrdadShirvani/AlibabaClone-Backend/blob/develop/AlibabaClone.Application/Services/AuthService.cs + + +## Configuring Jwt in Program.cs +- [ ] Register `JwtGenerator` service +```c# +builder.Services.AddScoped(); +``` +- [ ] Bind `JwtSettings` from configuration +```c# +builder.Services.Configure(builder.Configuration.GetSection("Jwt")); +``` +- [ ] Configure JWT authentication +```c# +var jwtSettings = builder.Configuration.GetSection("Jwt").Get(); +builder.Services.AddAuthentication("Bearer") + .AddJwtBearer("Bearer", options => + { + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidIssuer = jwtSettings.Issuer, + + ValidateAudience = true, + ValidAudience = jwtSettings.Audience, + + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.Key)), + + ValidateLifetime = true, + ClockSkew = TimeSpan.Zero + }; + }); + +builder.Services.AddAuthorization(); + +``` + +- [ ] Add `app.UseAuthentication` before `app.UseAuthorization` +```c# +app.UseAuthentication(); +app.UseAuthorization(); +``` +# Add ApiControllers + +- [ ] Create `AuthController` +πŸ“‚ Suggested Folder: WebApi/Controllers/`AuthController.cs` + +- [ ] Add Class and Constructor +```c# +private readonly IAuthService _authService; +private readonly IJwtGenerator _jwtGenerator; + +public AuthController(IAuthService authService, IJwtGenerator jwtGenerator) +{ + _authService = authService; + _jwtGenerator = jwtGenerator; +} +``` +- [ ] Add Register Method +```c# +public async Task Register(RegisterRequestDto request) +{ + var result = await _authService.RegisterAsync(request); + + if (!result.IsSuccess) + return BadRequest(result.ErrorMessage); + + var token = _jwtGenerator.GenerateToken(result.Data); + var response = new AuthResponseDto + { + PhoneNumber = result.Data.PhoneNumber, + Roles = result.Data.Roles, + Token = token + }; + + return Ok(response); +} +``` +- [ ] Add Login Method + +```c# + [HttpPost("login")] + public async Task Login(LoginRequestDto request) + { + var result = await _authService.LoginAsync(request); + + if (!result.IsSuccess) + return Unauthorized(result.ErrorMessage); + + var token = _jwtGenerator.GenerateToken(result.Data); + var response = new AuthResponseDto + { + PhoneNumber = result.Data.PhoneNumber, + Roles = result.Data.Roles, + Token = token + }; + + return Ok(response); + } +``` + +- [ ] Create AccountController +πŸ“‚ Suggested Folder: WebApi/Controllers/AccountController.cs +```c# +public class AccountController : ControllerBase +{ + [Authorize(Roles = "User")] + [HttpGet("profile")] + public IActionResult GetProfile() + { + return Ok("Hi there, hello"); + } +} +``` + +# Merge +- [ ] Create a PR and merge the current branch with develop + + + diff --git a/02_ProjectOrientedSessions/Session07/Session07 Frontend.md b/02_ProjectOrientedSessions/Session07/Session07 Frontend.md new file mode 100644 index 0000000..8c7553f --- /dev/null +++ b/02_ProjectOrientedSessions/Session07/Session07 Frontend.md @@ -0,0 +1,304 @@ + + + +Great β€” now we’re connecting both ends of your application. Let’s walk through **how authentication and authorization flow works on the frontend side (React)** when using **JWT-based authentication with a backend API**. + +--- + +## πŸ” Overall Frontend Authentication Flow (React + JWT) + +1. **User logs in**: + + - They submit credentials (username/password) via a login form. + + - Frontend sends a POST request to `/api/auth/login`. + + - Backend validates and responds with a **JWT** (and optionally a **refresh token**). + +2. **Frontend stores the token**: + + - This is usually saved in: + + - `localStorage` βœ… simple, persistent + + - or an **HttpOnly cookie** βœ… safer, but needs server support + +3. **Frontend sends the token on future API calls**: + + - Automatically attaches the token as a header: + + ```js + Authorization: Bearer + ``` + +4. **Frontend restricts access to protected pages**: + + - You read the token from storage. + + - Decode it to extract role/claims. + + - Use React Router + `PrivateRoute` (or similar) to guard access. + + +--- + +## 🧱 How to Restrict Pages in React (Role-Based Routing) + +### βœ… Step 1: Decode and Check the Token + +```bash +npm install jwt-decode +``` + +```js +import jwtDecode from "jwt-decode"; + +function getUserFromToken() { + const token = localStorage.getItem("token"); + if (!token) return null; + + try { + const decoded = jwtDecode(token); + return decoded; // contains roles, exp, username, etc. + } catch { + return null; + } +} +``` + +--- + +### βœ… Step 2: Create a Protected Route Component + +```jsx +import { Navigate } from "react-router-dom"; + +function PrivateRoute({ children, requiredRole }) { + const user = getUserFromToken(); + + if (!user) return ; + if (requiredRole && !user.role?.includes(requiredRole)) { + return ; + } + + return children; +} +``` + +--- + +### βœ… Step 3: Use It in Routing + +```jsx + + + + } +/> +``` + +--- + +## πŸ›‘οΈ Where to Store the JWT? + +|Method|Persistent?|Secure?|Notes| +|---|---|---|---| +|`localStorage`|βœ… Yes|❌ No (accessible to JS)|Good for simple apps| +|`sessionStorage`|❌ No (clears on tab close)|❌ No|| +|`HttpOnly cookie`|βœ… Yes|βœ… Yes|Needs backend to set cookie| + +> πŸ” **If your app is vulnerable to XSS, use `HttpOnly` cookies instead of localStorage**. + +--- + +## 🧷 Automatically Adding JWT to Requests + +You can use **Axios interceptors**: + +```js +// axios.js +import axios from "axios"; + +const api = axios.create({ + baseURL: "http://your-api-url.com/api", +}); + +api.interceptors.request.use(config => { + const token = localStorage.getItem("token"); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; +}); + +export default api; +``` + +Then use `api.get(...)` or `api.post(...)` in your services. + +--- + +## 🧠 Summary + +|Task|Solution| +|---|---| +|Protect route to logged-in users|Use custom `` with token check| +|Check if user has a role|Decode token and inspect `role` claim| +|Send token with API requests|Use Axios interceptors| +|Save token|LocalStorage (easy) or HttpOnly cookie (secure)| +|Keep user logged in|Token stays in localStorage or cookie| +|Handle logout|Clear the token from storage| + +--- + +Let me know if you want: + +- A sample React project with protected routes and JWT integration + +- How to implement auto-logout when token expires + +- How to use **refresh tokens** + + +Happy to walk you through any of it! + +That flow is **excellent** and matches exactly how modern applications with **JWT-based authentication** typically work. You're thinking about both UX (popup forms) and proper backend coordination β€” that's a solid start. + +--- + +## βœ… Your Flow β€” Reviewed & Annotated: + +### **1. Account Sign-Up (Register)** + +> **Frontend (React)**: Sign-up modal with: + +- Phone number + +- Password (+ confirm password) + +- Basic validations (empty fields, password length, etc.) + + +> **Backend (ASP.NET Core API)**: + +- Validate: Check if phone already exists + +- Validate password strength (you can set rules) + +- Hash the password securely (use `PasswordHasher` or Identity's helper) + +- Store the user in DB (with phone number as username or unique field) + +- Return success/failure + + +βœ… **Note**: Use `[ApiController]`, `[FromBody]`, and model validation on the backend for clean error reporting. + +--- + +### **2. Login Page (Popup)** + +> **Frontend (React)**: + +- Login modal with: + + - Phone number + + - Password + + +> **Backend**: + +- Validate credentials + +- If valid, generate: + + - **JWT** (access token) β€” includes user ID and roles + + - (Optional) Refresh token + +- Return the JWT token to the frontend + + +βœ… **Token Payload Example**: + +```json +{ + "sub": "user_id_here", + "phone": "09xxxxxxxxx", + "role": "Customer", + "exp": 1234567890 +} +``` + +βœ… You’ll send this token to the frontend in the response. + +--- + +### **3. Store the Token + Access Protected Page** + +> **Frontend**: + +- Store the token (in `localStorage` for now) + +- Use Axios to attach it on every request + +- Use a `PrivateRoute` component to guard protected pages + +- Access `/profile` page (protected by `[Authorize]` in backend) + + +> **Backend**: + +- Secure `/profile` with `[Authorize(Roles = "Customer")]` or similar + +- Parse token from `Authorization: Bearer ` header + +- Allow or reject based on claims + + +--- + +## πŸ› οΈ What You'll Need + +### Frontend: + +- `react-router-dom` + +- `axios` + +- `jwt-decode` + +- Modals (e.g., using Tailwind or a component lib) + + +### Backend: + +- ASP.NET Core API (you already have it) + +- JWT configuration in `Program.cs` + +- Custom authentication controller (`/api/auth/register`, `/api/auth/login`) + +- Role support + +- Token generation utility + + +--- + +## πŸš€ Ready to Begin? + +Let's start with **Step 1**: Sign-up flow + +Would you like to begin with: + +1. **Backend first** β€” setting up `/api/auth/register` endpoint and phone+password validation + +2. **Frontend first** β€” creating the sign-up modal form in React + + +Let me know your preference and we’ll go step by step. \ No newline at end of file diff --git a/02_ProjectOrientedSessions/Session07/Session07.md b/02_ProjectOrientedSessions/Session07/Session07.md deleted file mode 100644 index 87c908a..0000000 --- a/02_ProjectOrientedSessions/Session07/Session07.md +++ /dev/null @@ -1,82 +0,0 @@ - - -### πŸ”Ή 1. **User Sign-Up Flow** - -- React modal (SignUpModal component) - -- Fields: `PhoneNumber`, `Password`, `ConfirmPassword` - -- Frontend validation (empty fields, password length match, etc.) - -- Send request to `/api/auth/register` (POST) - -- Backend controller: - - - Check if user with phone exists - - - Validate password (min length, maybe complexity rules) - - - Hash password - - - Store new user in DB - - - Return success/failure - - -βœ… **Backend helper**: Use `PasswordHasher` to hash passwords securely -βœ… **Phone number** can act as username or unique field - ---- - -### πŸ”Ή 2. **User Login Flow** - -- React modal (LoginModal component) - -- Fields: `PhoneNumber`, `Password` - -- Submit to `/api/auth/login` (POST) - -- Backend checks: - - - Find user by phone - - - Verify password (using PasswordHasher) - - - Generate **JWT** - - - Return token (and optionally refresh token) - - -βœ… Token should include claims like user ID and role -βœ… Sign token with your secret key - ---- - -### πŸ”Ή 3. **Store Token + Access Protected Page** - -- Save token to `localStorage` or `cookie` - -- Attach it to future API requests using Axios interceptor - -- Use `jwt-decode` to extract roles and validate access in frontend - -- Restrict `/profile` page using custom React `PrivateRoute` - - -Backend: Secure `/api/profile` with `[Authorize]` (or `[Authorize(Roles = "X")]`) - ---- - -## πŸ›  Technologies to Use - -|Area|Tool| -|---|---| -|Frontend|React, Axios, React Router, jwt-decode| -|Backend|ASP.NET Core API, EF Core, JWT Bearer Auth| -|Security|PasswordHasher, Authorization attributes, Token signing key| - ---- - - -# Mention adding User Role in Database -# Becareful not to put Jwt inside another thing in the appsettings \ No newline at end of file