Class Name: SelfRegisterController¶
Last Updated: 2025-10-22 Source Code: https://github.com/AANP-IT/I2C.Salesforce.Metadata/blob/STAGING/force-app/main/default/classes/SelfRegisterController.cls
API Name: SelfRegisterController Type: Controller (Experience Cloud) Test Coverage: To be determined
Business Purpose¶
The SelfRegisterController manages user authentication for Experience Cloud sites, providing:
- Custom login functionality with username validation
- Password reset capabilities
- Login attempt tracking to prevent brute force attacks
- Integration with Salesforce Site.login() and Site.forgotPassword() methods
This supports secure self-service user authentication for community portals.
Class Overview¶
- Author: Ion Cernenchii
- Created: 09-APR-2024
- Last Modified: 15-MAY-2024
- Scope/Sharing:
without sharing- CRITICAL SECURITY CONCERN - Key Responsibilities:
- User login with validation
- Password reset requests
- Login attempt tracking
- Username validation
⚠️ CRITICAL: WITHOUT SHARING¶
Uses without sharing which bypasses record-level security. This allows:
- Querying any User record regardless of permissions
- Updating login attempts for any user
- Potential security vulnerability
Justification: Likely needed for Experience Cloud guest user context (no user context), but should be carefully reviewed.
Public Methods¶
login¶
Purpose: Authenticates user and returns redirect URL.
Parameters:
- username (String) - User's username
- password (String) - User's password
- startUrl (String) - URL to redirect after successful login
Returns: String - Redirect URL or "Invalid Username"
Business Logic: 1. Username Validation (line 21):
Boolean isValidUsername = false;
if (Test.isRunningTest()) { isValidUsername = true; } else { isValidUsername = validUsername(username); }
- Login Attempt (line 23):
- Uses Salesforce Site.login() method
-
Returns PageReference with redirect URL
-
Return URL (line 24):
- Returns redirect URL in production
- Returns startUrl in tests
Issues/Concerns: - 🚨 Test.isRunningTest() in Production (lines 21, 24): Anti-pattern - Production code should not behave differently in tests - Use dependency injection or test-specific data instead - ⚠️ No Error Handling: Site.login() can throw exceptions - ⚠️ Null Pointer Risk: lgn could be null if login fails - ⚠️ No Login Attempt Tracking: Doesn't call updateLoginAttempts() on failure - ✅ Username Pre-Validation: Checks username exists before attempting login
forgotPassword¶
Purpose: Initiates password reset process.
Parameters:
- email (String) - User's email address
Returns: Boolean - true if successful, false otherwise
Business Logic: - Delegates to Site.forgotPassword() - Sends password reset email
Issues/Concerns:
- ⚠️ No Error Handling: Exception not caught
- ⚠️ Parameter Name Mismatch: Parameter named email but doc says username
- ✅ Simple Delegation: Leverages Salesforce built-in functionality
validUsername¶
Purpose: Checks if username exists in system.
Parameters:
- username (String) - Username to validate
Returns: Boolean - true if user exists, false otherwise
Business Logic: 1. Checks for blank username (line 45) 2. Queries User object for matching username (line 46) 3. Returns true if user found
Issues/Concerns: - ⚠️ No Error Handling: Query exception not caught - ⚠️ Security Risk: Allows username enumeration - Attackers can discover valid usernames - Consider rate limiting or CAPTCHA - ✅ Efficient Query: Uses LIMIT 1 for performance
getCurrentUserLoginAttempts¶
Purpose: Retrieves login attempt count for user.
Parameters:
- username (String) - Username to check
Returns: Integer - Number of login attempts (0 if none or user not found)
Business Logic: 1. Returns 0 for blank username (line 52) 2. Queries User.Login_Attempts__c field (line 53) 3. Returns count or 0 if null
Issues/Concerns: - ⚠️ No Error Handling: Query exception not caught (user not found) - ⚠️ Custom Field: Requires Login_Attempts__c custom field on User - ✅ Null-Safe: Returns 0 if field is null
updateLoginAttempts¶
Purpose: Increments login attempt counter for user.
Parameters:
- username (String) - Username to update
Returns: String - Success message or error
Business Logic: 1. Returns error for blank username (line 59) 2. Queries user record (line 61) 3. Increments Login_Attempts__c (lines 62-66): - Sets to 1 if null or 0 - Otherwise increments by 1 4. Updates user record (line 67)
Issues/Concerns: - 🚨 Test.isRunningTest() in DML (line 67): Bypasses update in tests - Should update in tests to verify functionality - ⚠️ No Maximum Limit: Doesn't cap login attempts - Should have maximum (e.g., 5) before account lockout - ⚠️ Race Condition: Concurrent logins could lose increment - Two simultaneous failures might only increment once - ✅ Error Handling: Catches and wraps exceptions
resetLoginAttempts¶
Purpose: Resets login attempt counter to 0.
Parameters:
- username (String) - Username to reset
Returns: String - Success message or error
Business Logic: 1. Returns error for blank username (line 76) 2. Queries user record (line 78) 3. Sets Login_Attempts__c to 0 (line 79) 4. Updates user record (line 83)
Issues/Concerns: - 🚨 Test.isRunningTest() in DML (line 80): Bypasses update in tests - ⚠️ Called After Successful Login?: Documentation doesn't clarify when to call - ✅ Error Handling: Catches and wraps exceptions
Dependencies¶
Salesforce Objects¶
- User (Standard Object)
- Fields:
Id,Username,Login_Attempts__c(custom) - Access: Read, Update
Custom Settings/Metadata¶
- None
Other Classes¶
- Site (Salesforce Class): login(), forgotPassword()
Design Patterns¶
- Facade Pattern: Wraps Salesforce Site methods
- LWC Controller Pattern: @AuraEnabled for Lightning components
- Counter Pattern: Tracks login attempts
Governor Limits Considerations¶
Current Impact¶
- SOQL Queries: 1-2 per method call
- DML Statements: 1 per update/reset
- Heap Size: Minimal
Security Considerations¶
🚨 CRITICAL ISSUES¶
- WITHOUT SHARING (line 6): Bypasses all record-level security
- Can query/update any User record
-
Required for guest user context but risky
-
Username Enumeration (line 44): validUsername allows attackers to discover valid usernames
- Add rate limiting
- Consider CAPTCHA after N attempts
-
Return generic error messages
-
Test.isRunningTest() in Production (lines 21, 24, 67, 80): Anti-pattern
- Different behavior in tests vs production
-
Breaks test validity
-
No Login Attempt Lockout: Unlimited login attempts
- Add maximum attempts (e.g., 5)
- Implement temporary account lockout
-
Add CAPTCHA after 3 attempts
-
Race Condition: Login_Attempts__c updates not atomic
- Two concurrent failures might lose count
- Consider platform event for async tracking
Test Class Requirements¶
@IsTest
public class SelfRegisterControllerTest {
@IsTest
static void testLogin_Success() {
// Test successful login
}
@IsTest
static void testLogin_InvalidUsername() {
String result = SelfRegisterController.login('invalid@user.com', 'password', '/home');
Assert.areEqual('Invalid Username', result);
}
@IsTest
static void testValidUsername() {
User u = [SELECT Username FROM User WHERE IsActive = true LIMIT 1];
Boolean valid = SelfRegisterController.validUsername(u.Username);
Assert.isTrue(valid);
}
@IsTest
static void testUpdateLoginAttempts() {
User u = [SELECT Username FROM User WHERE IsActive = true LIMIT 1];
String result = SelfRegisterController.updateLoginAttempts(u.Username);
// Note: Current implementation bypasses update in tests
}
}
Pre-Go-Live Concerns¶
🚨 CRITICAL¶
- WITHOUT SHARING: Review security implications
- Username Enumeration: Add rate limiting/CAPTCHA
- Test.isRunningTest(): Remove from production code
- No Login Lockout: Implement maximum attempts
HIGH¶
- No Error Handling: Add try-catch to all methods
- Race Conditions: Fix concurrent update issues
- Null Pointer Risks: Check for null lgn in login()
MEDIUM¶
- No Maximum Attempts: Cap at 5-10 attempts
- Custom Field Dependency: Document Login_Attempts__c requirement
Changes & History¶
| Date | Author | Description |
|---|---|---|
| 2024-04-09 | Ion Cernenchii | Initial implementation |
| 2024-05-15 | Ion Cernenchii | Added forgotPassword method |
Documentation Status: ✅ Complete Code Review Status: 🚨 CRITICAL - Multiple security issues Test Coverage: Test class needed