Skip to content

vaa2000/Docker-Kubernetes

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 

Repository files navigation

Password Expiration & Change Flow - Testing Guide

Overview

Features Implemented

1. Password Expiration Warning System

  • Backend calculates password age based on PasswordUpdatedAt timestamp
  • Warning shown when: (Password_expiration_duration - password_age) <= Password_expiration_warning
  • Frontend displays dialog with human-readable time (days/hours)
  • Users can choose "Change Password Now" or "Remind Me Later"

2. Password Change Flow

  • Dedicated ChangePassword component with validation
  • Backend endpoint validates current password before allowing change
  • Enforces password complexity requirements
  • Prevents reusing the same password
  • Audit trail logging for password changes

3. Account Lockout System

  • Invalid login attempts tracked per user
  • Account locks after exceeding allowed attempts
  • Automatic unlock after lockout period expires
  • Manual unlock available for administrators

Test Configuration

Current Policy Settings (from LevelwisePermissions)

{
  "Login_Inactivity_Timeout": 300,           // 5 minutes
  "Invalid_Login_Attempts_Allowed": 3,       // 3 attempts
  "Invalid_Login_Lockout_Period": 14400,     // 4 hours
  "Password_expiration_duration": 2592000,   // 30 days
  "Password_expiration_warning": 604800      // 7 days
}

Password Complexity Requirements

  • Minimum 8 characters
  • At least one uppercase letter (A-Z)
  • At least one lowercase letter (a-z)
  • At least one number (0-9)
  • At least one special character (@$!%*?&)

Testing Scenarios

Scenario 1: Valid Login (No Warnings)

Objective: Verify normal login flow when password is not near expiration

Preconditions:

  • User exists with valid credentials
  • Password age < (Password_expiration_duration - Password_expiration_warning)
  • Account not locked

Steps:

  1. Navigate to login page
  2. Enter valid email and password
  3. Click "Sign In"
  4. Enter OTP received via email
  5. Click "Verify OTP"

Expected Result:

  • ✅ OTP sent successfully
  • ✅ User redirected to home page
  • ✅ No password expiration warning shown
  • ✅ Inactivity timeout set correctly

Database Check:

// Password age calculation
const passwordAge = Math.floor(Date.now()/1000) - user.PasswordUpdatedAt;
const daysUntilExpiry = Password_expiration_duration - passwordAge;
// Should be > Password_expiration_warning (604800 seconds / 7 days)

Scenario 2: Password Expiration Warning

Objective: Verify warning dialog appears when password is near expiration

Preconditions:

  • User exists with valid credentials
  • Password age = Password_expiration_duration - Password_expiration_warning (or less)
  • Example: For 30-day expiration with 7-day warning, password should be 23+ days old

Test Data Setup:

// Manually update user in Cosmos DB to simulate old password
{
  "PasswordUpdatedAt": Math.floor(Date.now()/1000) - 2000000  // ~23 days ago
}

Steps:

  1. Navigate to login page
  2. Enter valid email and password
  3. Click "Sign In"
  4. Enter OTP received via email
  5. Click "Verify OTP"

Expected Result:

  • ✅ Password warning dialog appears
  • ✅ Dialog shows: "Your password will expire in X days and Y hours"
  • ✅ Two buttons: "Change Password Now" and "Remind Me Later"
  • ✅ Calculation accurate (converts seconds to days/hours)

Calculation Verification:

// Frontend calculation in Loginpage.js
const seconds = res.data.data.daysUntilExpiry;  // From backend
const days = Math.floor(seconds / 86400);
const hours = Math.floor((seconds % 86400) / 3600);
// Example: 592800 seconds = 6 days, 21 hours

Option A - "Remind Me Later":

  • ✅ Dialog closes
  • ✅ User redirected to home page
  • ✅ Can continue using application normally
  • ✅ Warning will appear on next login

Option B - "Change Password Now":

  • ✅ Dialog closes
  • ✅ User redirected to /changepassword page
  • ✅ Email field pre-filled and disabled

Scenario 3: Password Expired

Objective: Verify login blocked when password has expired

Test Data Setup:

// Update user in Cosmos DB
{
  "PasswordUpdatedAt": Math.floor(Date.now()/1000) - 2700000,  // ~31 days ago
  "PasswordExpired": true
}

Steps:

  1. Navigate to login page
  2. Enter valid email and password (even if correct)
  3. Click "Sign In"
  4. Enter OTP
  5. Click "Verify OTP"

Expected Result:

  • ✅ OTP verification fails
  • ✅ Error message: "Your password has expired"
  • ✅ User NOT logged in
  • ✅ Must use "Forgot Password" flow to reset

Backend Response:

{
  state: 0,
  username: "",
  userdetails: {},
  message: "Password Expired"
}

Scenario 4: Change Password (Authenticated User)

Objective: Verify user can successfully change password

Preconditions:

  • User logged in (has valid session token)
  • Knows current password

Steps:

  1. Navigate to /changepassword (from warning dialog or manually)
  2. Email field should be pre-filled and disabled
  3. Enter current password: CurrentPass123!
  4. Enter new password: NewSecurePass456@
  5. Enter confirm password: NewSecurePass456@
  6. Click "Change Password"

Expected Result:

  • ✅ Backend validates current password
  • ✅ Backend checks password complexity
  • PasswordUpdatedAt timestamp updated in database
  • PasswordExpired flag set to false
  • ✅ Success dialog: "Password changed successfully! Please login with your new password."
  • ✅ User redirected to login page
  • ✅ Session cleared
  • ✅ Audit trail logged

Database Changes:

// User document after successful change
{
  "Password": "NewSecurePass456@",  // New password
  "PasswordUpdatedAt": 1734774000,   // Current timestamp
  "PasswordExpired": false,
  "CurrentInvalidLoginAttemptCount": 0  // Reset counter
}

Scenario 5: Change Password Validation Errors

5A: Password Mismatch

Steps:

  • New password: NewPass123!
  • Confirm password: DifferentPass456@

Expected Result:

  • ✅ Error: "New password and confirm password do not match"
  • ✅ Form not submitted

5B: Same as Current Password

Steps:

  • Current password: CurrentPass123!
  • New password: CurrentPass123!

Expected Result:

  • ✅ Error: "New password must be different from current password"
  • ✅ Backend rejects the request

5C: Weak Password (No Uppercase)

Steps:

  • New password: weakpass123!

Expected Result:

  • ✅ Error: "Password must be at least 8 characters long and contain uppercase, lowercase, number & special character"

5D: Weak Password (No Special Character)

Steps:

  • New password: WeakPass123

Expected Result:

  • ✅ Same complexity error as above

5E: Wrong Current Password

Steps:

  • Current password: WrongPassword123!
  • New password: NewPass456@

Expected Result:

  • ✅ Backend error: "Current password is incorrect"
  • ✅ HTTP 401 status

Scenario 6: Invalid Login Attempts & Account Lockout

Objective: Verify account locks after exceeding invalid attempts

Configuration:

  • Invalid_Login_Attempts_Allowed: 3
  • Invalid_Login_Lockout_Period: 14400 seconds (4 hours)

Steps:

  1. Attempt login with wrong password (Attempt 1)
  2. Attempt login with wrong password (Attempt 2)
  3. Attempt login with wrong password (Attempt 3)
  4. Attempt login with correct password (Attempt 4)

Expected Results:

After Attempt 1:

  • ✅ Error: "Invalid email or password"
  • CurrentInvalidLoginAttemptCount: 1
  • LastInvalidLoginAttempt: [current timestamp]

After Attempt 2:

  • ✅ Error: "Invalid email or password"
  • CurrentInvalidLoginAttemptCount: 2

After Attempt 3:

  • ✅ Error: "Invalid email or password"
  • CurrentInvalidLoginAttemptCount: 3
  • UserAccountDisabled: true
  • AccountLockedAt: [current timestamp]

After Attempt 4 (Even with Correct Password):

  • ✅ Error: "User Account is Disabled. Your account will be unlocked at [time]"
  • ✅ Login blocked even with correct credentials
  • ✅ Shows exact unlock time formatted as HH:MM:SS

Database State After Lockout:

{
  "CurrentInvalidLoginAttemptCount": 3,
  "LastInvalidLoginAttempt": 1734770000,
  "UserAccountDisabled": true,
  "AccountLockedAt": 1734770000
}

Scenario 7: Account Auto-Unlock After Lockout Period

Objective: Verify account automatically unlocks after lockout period expires

Preconditions:

  • Account locked (UserAccountDisabled: true)
  • AccountLockedAt timestamp set
  • Wait for lockout period to expire (or manually adjust timestamp)

Test Data Manipulation:

// Set lockout time to 5 hours ago (past the 4-hour lockout period)
{
  "AccountLockedAt": Math.floor(Date.now()/1000) - 18000  // 5 hours ago
}

Steps:

  1. Wait for lockout period to expire (or use test data above)
  2. Attempt login with correct credentials
  3. Enter OTP

Expected Result:

  • ✅ Backend checks: (current_time - AccountLockedAt) > Invalid_Login_Lockout_Period
  • ✅ Account automatically unlocked
  • UserAccountDisabled set to false
  • CurrentInvalidLoginAttemptCount reset to 0
  • ✅ Login proceeds normally
  • ✅ User redirected to home page

Database State After Auto-Unlock:

{
  "CurrentInvalidLoginAttemptCount": 0,
  "LastInvalidLoginAttempt": 1734770000,
  "UserAccountDisabled": false,
  "AccountLockedAt": null  // or previous value, doesn't matter
}

Scenario 8: Successful Login Resets Invalid Attempt Counter

Objective: Verify counter resets after successful login

Preconditions:

  • User has 2 invalid attempts (CurrentInvalidLoginAttemptCount: 2)
  • Not yet locked

Steps:

  1. Login with correct credentials
  2. Enter OTP
  3. Verify OTP

Expected Result:

  • ✅ Login successful
  • CurrentInvalidLoginAttemptCount reset to 0
  • ✅ No lockout occurs
  • ✅ Counter starts fresh for future attempts

Scenario 9: Forgot Password Flow (Password Expired)

Objective: Verify users can reset expired passwords via email

Preconditions:

  • Password expired
  • Cannot login normally

Steps:

  1. Click "Forgot Password?" on login page
  2. Enter email address
  3. Click "Submit"
  4. Check email for reset link
  5. Click link (redirects to /reset-password)
  6. Enter new password
  7. Confirm new password
  8. Click "Reset Password"

Expected Result:

  • ✅ Reset link email sent
  • ✅ Reset page loads
  • ✅ Password complexity validated
  • PasswordUpdatedAt updated
  • PasswordExpired set to false
  • ✅ Success message shown
  • ✅ Can now login with new password

Backend Testing (Unit Tests)

Test checkuserdetails() Function

// Test 1: Valid credentials, password not expiring
describe('checkuserdetails - Valid Login', () => {
  it('should return user details when credentials are correct', async () => {
    const mockUser = {
      Email: 'test@example.com',
      Password: 'ValidPass123!',
      PasswordUpdatedAt: Math.floor(Date.now()/1000) - 86400, // 1 day old
      UserAccountDisabled: false,
      PasswordExpired: false
    };
    
    // Mock Cosmos DB query
    const result = await checkuserdetails('test@example.com', 'ValidPass123!');
    
    expect(result.state).toBe(1);
    expect(result.username).toBe('test@example.com');
    expect(result.passwordWarning).toBeUndefined();
  });
});

// Test 2: Password near expiration
describe('checkuserdetails - Password Warning', () => {
  it('should return password warning when near expiration', async () => {
    const mockUser = {
      Email: 'test@example.com',
      Password: 'ValidPass123!',
      PasswordUpdatedAt: Math.floor(Date.now()/1000) - 2000000, // 23 days old
      UserAccountDisabled: false,
      PasswordExpired: false
    };
    
    const result = await checkuserdetails('test@example.com', 'ValidPass123!');
    
    expect(result.state).toBe(1);
    expect(result.passwordWarning).toBe(true);
    expect(result.daysUntilExpiry).toBeGreaterThan(0);
    expect(result.daysUntilExpiry).toBeLessThanOrEqual(604800); // Within warning period
  });
});

// Test 3: Expired password
describe('checkuserdetails - Password Expired', () => {
  it('should block login when password expired', async () => {
    const mockUser = {
      Email: 'test@example.com',
      Password: 'ValidPass123!',
      PasswordUpdatedAt: Math.floor(Date.now()/1000) - 2700000, // 31 days old
      UserAccountDisabled: false,
      PasswordExpired: true
    };
    
    const result = await checkuserdetails('test@example.com', 'ValidPass123!');
    
    expect(result.state).toBe(0);
    expect(result.message).toBe('Password Expired');
  });
});

// Test 4: Invalid password increments counter
describe('checkuserdetails - Invalid Password', () => {
  it('should increment invalid login counter', async () => {
    const mockUser = {
      Email: 'test@example.com',
      Password: 'ValidPass123!',
      CurrentInvalidLoginAttemptCount: 1
    };
    
    const result = await checkuserdetails('test@example.com', 'WrongPassword!');
    
    expect(result.state).toBe(0);
    expect(result.message).toContain('Invalid email or password');
    // Verify DB updated: CurrentInvalidLoginAttemptCount should be 2
  });
});

// Test 5: Account locked
describe('checkuserdetails - Account Locked', () => {
  it('should block login when account is locked', async () => {
    const mockUser = {
      Email: 'test@example.com',
      Password: 'ValidPass123!',
      UserAccountDisabled: true,
      AccountLockedAt: Math.floor(Date.now()/1000) - 3600, // 1 hour ago
      CurrentInvalidLoginAttemptCount: 3
    };
    
    const result = await checkuserdetails('test@example.com', 'ValidPass123!');
    
    expect(result.state).toBe(0);
    expect(result.message).toContain('User Account is Disabled');
  });
});

// Test 6: Account auto-unlock
describe('checkuserdetails - Auto Unlock', () => {
  it('should automatically unlock account after lockout period', async () => {
    const mockUser = {
      Email: 'test@example.com',
      Password: 'ValidPass123!',
      UserAccountDisabled: true,
      AccountLockedAt: Math.floor(Date.now()/1000) - 18000, // 5 hours ago (past 4-hour lockout)
      CurrentInvalidLoginAttemptCount: 3
    };
    
    const result = await checkuserdetails('test@example.com', 'ValidPass123!');
    
    expect(result.state).toBe(1);
    // Verify DB updated: UserAccountDisabled = false, CurrentInvalidLoginAttemptCount = 0
  });
});

Manual Testing Checklist

Pre-Test Setup

  • Backend server running
  • Frontend development server running
  • Access to Cosmos DB to modify test data
  • Email service configured for OTP delivery
  • Test user accounts created

Password Warning Tests

  • Test 1: Normal login (no warning)
  • Test 2: Password warning appears (7 days before expiry)
  • Test 3: Warning calculation accurate (days/hours correct)
  • Test 4: "Remind Me Later" continues to home
  • Test 5: "Change Password Now" navigates to change page

Password Expiration Tests

  • Test 6: Expired password blocks login
  • Test 7: Error message displays correctly
  • Test 8: Can reset via "Forgot Password"

Change Password Tests

  • Test 9: Email pre-filled and disabled
  • Test 10: Current password validated
  • Test 11: Password mismatch error
  • Test 12: Same password error
  • Test 13: Weak password error (no uppercase)
  • Test 14: Weak password error (no special char)
  • Test 15: Successful password change
  • Test 16: Redirected to login after change
  • Test 17: Can login with new password
  • Test 18: Cannot login with old password

Account Lockout Tests

  • Test 19: Invalid attempt counter increments
  • Test 20: Account locks after 3 invalid attempts
  • Test 21: Cannot login even with correct password when locked
  • Test 22: Lockout message shows unlock time
  • Test 23: Account auto-unlocks after lockout period
  • Test 24: Successful login resets counter

Database Verification

  • PasswordUpdatedAt updates on password change
  • PasswordExpired flag resets on password change
  • CurrentInvalidLoginAttemptCount increments/resets correctly
  • UserAccountDisabled toggles correctly
  • AccountLockedAt timestamp set correctly
  • LastInvalidLoginAttempt timestamp updates

Audit Trail Verification

  • Password change logged to audit trail
  • Login/logout events logged
  • User ID, Project ID, Org ID captured correctly
  • Timestamps accurate

Performance Testing

Load Test Scenarios

  1. Concurrent Logins: 100 users login simultaneously
  2. OTP Generation: Verify email service handles load
  3. Password Expiration Check: Database query performance
  4. Account Lockout: Multiple failed attempts from different users

Expected Performance Metrics

  • Login response time: < 2 seconds
  • OTP delivery: < 30 seconds
  • Password change: < 1 second
  • Database query time: < 500ms

Security Testing

Penetration Testing Scenarios

  • Attempt SQL injection in email field
  • Attempt XSS in password fields
  • Brute force password guessing (should trigger lockout)
  • Session hijacking attempts
  • JWT token tampering
  • CSRF attack prevention
  • Rate limiting on login endpoint

Password Storage Verification

  • Passwords not stored in plain text (should be hashed)
  • Audit trail does not log actual passwords (shows *****)
  • Console logs do not expose sensitive data in production

Troubleshooting Guide

Issue: Password warning not appearing

Possible Causes:

  1. PasswordUpdatedAt timestamp incorrect in database
  2. Password_expiration_warning setting too low
  3. Frontend calculation error
  4. Backend not returning passwordWarning flag

Debug Steps:

// Backend console.log in userdatabase.js
console.log("Password Age:", passwordAge);
console.log("Days Until Expiry:", daysUntilExpiry);
console.log("Warning Threshold:", Password_expiration_warning);

// Frontend console.log in Loginpage.js
console.log("Response data:", res.data.data);
console.log("Password Warning:", res.data.data.passwordWarning);
console.log("Days Until Expiry (seconds):", res.data.data.daysUntilExpiry);

Issue: Account lockout not working

Possible Causes:

  1. Invalid_Login_Attempts_Allowed setting incorrect
  2. Counter not incrementing in database
  3. Logic error in checkuserdetails() function

Debug Steps:

// Check user document in Cosmos DB
{
  "CurrentInvalidLoginAttemptCount": ?,  // Should increment
  "LastInvalidLoginAttempt": ?,          // Should update
  "UserAccountDisabled": ?,              // Should become true
  "AccountLockedAt": ?                   // Should set timestamp
}

Issue: Change password fails

Possible Causes:

  1. Backend endpoint not reachable
  2. Current password validation failing
  3. Token not passed correctly
  4. Database update failing

Debug Steps:

// Check network tab in browser DevTools
// POST /login/changePassword
// Request payload should contain: email, currentPassword, newPassword

// Backend logs
logger.info("Change password request for:", email);
logger.info("Current password valid:", userCheck.state);
logger.info("Update result:", isUpdated);

Conclusion

This comprehensive testing guide covers all aspects of the password expiration and change flow. Follow each scenario systematically to ensure the feature works correctly in all edge cases.

Key Success Criteria

✅ Users receive timely warnings before password expiration ✅ Expired passwords block login appropriately
✅ Change password flow is secure and user-friendly ✅ Account lockout prevents brute force attacks ✅ Auto-unlock works after lockout period ✅ Audit trail captures all password-related events ✅ No sensitive data exposed in logs or client-side

Next Steps

  1. Execute all manual test scenarios
  2. Write automated tests for backend functions
  3. Set up monitoring for password expiration events
  4. Create user documentation for password policies
  5. Schedule periodic security audits

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published