Skip to main content

Overview

The User model handles authentication and user account management for the SupermarketWEB application. It stores user credentials with validation attributes to ensure proper email format and secure password handling.

Class Definition

using System.ComponentModel.DataAnnotations;

namespace SupermarketWEB.Models
{
    public class User
    {
        public int Id { get; set; }
        [Required]
        [DataType(DataType.EmailAddress)]
        public string Email { get; set; }
        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; }
    }
}

Properties

Id
int
required
Primary key and unique identifier for the user account. Auto-generated by the database.
Email
string
required
User’s email address used for authentication and identification. Must be a valid email format.Data Annotations:
  • [Required] - Email is mandatory for account creation
  • [DataType(DataType.EmailAddress)] - Provides email-specific validation and UI hints
Password
string
required
User’s password for authentication. Should be hashed before storage.Data Annotations:
  • [Required] - Password is mandatory for account creation
  • [DataType(DataType.Password)] - Ensures password input fields render with masking

Data Annotations

The User model uses two critical data annotation attributes:

Required Attribute

[Required]
  • Ensures the field cannot be null or empty
  • Generates client-side and server-side validation
  • Returns validation error if field is missing

DataType Attribute

[DataType(DataType.EmailAddress)]
public string Email { get; set; }

// Provides:
// - Email format validation
// - HTML5 email input type in forms
// - Client-side email validation
The DataType attribute doesn’t perform actual validation; it provides UI rendering hints and metadata. Use additional validation attributes like [EmailAddress] for strict email validation.

Security Considerations

CRITICAL: Never store passwords in plain text. Always hash passwords using a secure algorithm before saving to the database.

Password Hashing Example

using System.Security.Cryptography;
using System.Text;

public class PasswordHasher
{
    public static string HashPassword(string password)
    {
        using (var sha256 = SHA256.Create())
        {
            var hashedBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(password));
            return Convert.ToBase64String(hashedBytes);
        }
    }

    public static bool VerifyPassword(string password, string hashedPassword)
    {
        var hashOfInput = HashPassword(password);
        return hashOfInput == hashedPassword;
    }
}

// Usage
var user = new User
{
    Email = "[email protected]",
    Password = PasswordHasher.HashPassword("SecurePass123!")
};
For production applications, use ASP.NET Core Identity:
using Microsoft.AspNetCore.Identity;

public class ApplicationUser : IdentityUser
{
    // Additional properties
}

// In Startup.cs or Program.cs
services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<SupermarketContext>()
    .AddDefaultTokenProviders();

Usage Examples

Creating a New User

var user = new User
{
    Email = "[email protected]",
    Password = PasswordHasher.HashPassword("MySecurePassword123!")
};

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

User Authentication

public async Task<User> AuthenticateUser(string email, string password)
{
    var user = await context.Users
        .FirstOrDefaultAsync(u => u.Email == email);

    if (user == null)
        return null;

    // Verify password
    if (PasswordHasher.VerifyPassword(password, user.Password))
        return user;

    return null;
}

// Usage
var authenticatedUser = await AuthenticateUser("[email protected]", "MyPassword");
if (authenticatedUser != null)
{
    // Login successful
    Console.WriteLine($"Welcome, {authenticatedUser.Email}");
}
else
{
    // Login failed
    Console.WriteLine("Invalid credentials");
}

Checking if Email Exists

public async Task<bool> EmailExists(string email)
{
    return await context.Users
        .AnyAsync(u => u.Email == email);
}

// Usage during registration
if (await EmailExists(newEmail))
{
    // Email already registered
    return BadRequest("Email already in use");
}

Updating User Email

public async Task<bool> UpdateEmail(int userId, string newEmail)
{
    // Check if new email is already taken
    if (await context.Users.AnyAsync(u => u.Email == newEmail && u.Id != userId))
        return false;

    var user = await context.Users.FindAsync(userId);
    if (user == null)
        return false;

    user.Email = newEmail;
    await context.SaveChangesAsync();
    return true;
}

Changing Password

public async Task<bool> ChangePassword(int userId, string currentPassword, string newPassword)
{
    var user = await context.Users.FindAsync(userId);
    if (user == null)
        return false;

    // Verify current password
    if (!PasswordHasher.VerifyPassword(currentPassword, user.Password))
        return false;

    // Update to new password
    user.Password = PasswordHasher.HashPassword(newPassword);
    await context.SaveChangesAsync();
    return true;
}

Password Reset

public async Task<string> GeneratePasswordResetToken(string email)
{
    var user = await context.Users
        .FirstOrDefaultAsync(u => u.Email == email);
    
    if (user == null)
        return null;

    // Generate secure token (simplified example)
    var token = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));
    
    // Store token with expiration (requires additional table/properties)
    // Send token via email
    
    return token;
}

public async Task<bool> ResetPassword(string token, string newPassword)
{
    // Validate token and get user
    // Update password
    // Invalidate token
    
    return true;
}

Database Schema

When migrated to the database, the User table has the following structure:
ColumnTypeNullableKey
IdintNoPrimary Key (Identity)
Emailnvarchar(MAX)No
Passwordnvarchar(MAX)No
Add a unique index on the Email field to prevent duplicate accounts:
modelBuilder.Entity<User>()
    .HasIndex(u => u.Email)
    .IsUnique();

Enhanced Model with Validation

For production use, enhance the model with additional validation:
using System.ComponentModel.DataAnnotations;

public class User
{
    public int Id { get; set; }
    
    [Required(ErrorMessage = "Email is required")]
    [EmailAddress(ErrorMessage = "Invalid email format")]
    [StringLength(200)]
    public string Email { get; set; }
    
    [Required(ErrorMessage = "Password is required")]
    [StringLength(100, MinimumLength = 8, ErrorMessage = "Password must be at least 8 characters")]
    [DataType(DataType.Password)]
    public string Password { get; set; }
}

Extended User Model

For a complete user management system, consider extending the model:
public class User
{
    public int Id { get; set; }
    
    [Required]
    [EmailAddress]
    public string Email { get; set; }
    
    [Required]
    [DataType(DataType.Password)]
    public string Password { get; set; }
    
    // Additional properties
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public bool IsActive { get; set; } = true;
    public bool EmailConfirmed { get; set; } = false;
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    public DateTime? LastLoginAt { get; set; }
    public string Role { get; set; } = "User";
    public int FailedLoginAttempts { get; set; } = 0;
    public DateTime? LockedUntil { get; set; }
}

Authentication Context

The User model works within the authentication flow:
  1. Registration: Create new user with hashed password
  2. Login: Verify credentials against stored hash
  3. Session Management: Maintain authenticated state
  4. Authorization: Check user permissions based on role
  5. Password Reset: Secure token-based password recovery

Best Practices

  1. Password Hashing: Always use bcrypt, PBKDF2, or Argon2 for password hashing
  2. Salt Passwords: Use unique salts for each password hash
  3. Email Uniqueness: Enforce unique constraint on email field
  4. Password Complexity: Require minimum length, special characters, numbers
  5. Account Lockout: Implement lockout after failed login attempts
  6. Email Verification: Require email confirmation before activation
  7. Two-Factor Authentication: Consider implementing 2FA for enhanced security
  8. Password History: Prevent reuse of recent passwords
  9. Session Management: Implement secure session handling with timeouts
  10. Audit Logging: Track authentication events and suspicious activities

Security Checklist

  • Passwords are hashed using secure algorithm (bcrypt/Argon2)
  • Email addresses are validated and normalized
  • Unique constraint on email field
  • Password complexity requirements enforced
  • Account lockout after failed attempts
  • Email verification implemented
  • Secure password reset flow
  • HTTPS enforced for all authentication endpoints
  • Protection against brute force attacks
  • Regular security audits

Build docs developers (and LLMs) love