Component Name: loginForm¶
Last Updated: 2025-10-22 Source Code: .temp-staging-flows/force-app/main/default/lwc/loginForm/
API Name: c-loginForm Type: Form Component Target: lightningCommunity__Page, lightningCommunity__Default
Business Purpose¶
The loginForm component provides the primary authentication interface for AANP's member portal. It features progressive security measures including CAPTCHA activation after failed login attempts and automatic account lockout protection. The component handles both guest and authenticated user states, supports PAC contribution workflow redirects, and integrates with Salesforce Site authentication services.
User Interface¶
Visual Description¶
- Layout: Centered single-column form with max-width 600px
- Key UI Elements:
- Email input field with validation
- Password input field with validation
- Dynamic error messages (red backgrounds for field errors, textarea for critical errors)
- reCAPTCHA component (appears after failed login attempts)
- Login button with loading state
- "Forgot Your Password?" link
- "Create an Account" link
- Terms of Use and Privacy Policy links
- For authenticated users: Welcome message, Logout button, quick navigation links
- Responsive: Full-width on mobile with horizontal centering, maintains 600px max-width on desktop
Screenshots¶
- Desktop view: Centered form card on light background
- Mobile view: Full-width form with maintained padding
- Logged-in view: Welcome message with navigation options
Component Structure¶
Files¶
loginForm.html- Template/markup with conditional rendering for guest/authenticated statesloginForm.js- JavaScript controller with authentication logic and progressive securityloginForm.css- Styling for form and buttonsloginForm.js-meta.xml- Metadata configuration for Experience Cloud
HTML Template Structure¶
<template>
<template lwc:if={isGuestUser}>
<form class="login-form bg-light" onsubmit={handleSubmit}>
<!-- Error textarea for failed attempts -->
<template if:true={showTextarea}>
<textarea readonly class="...border-danger bg-danger-subtle">
{errors.invalidCredentials}
</textarea>
</template>
<!-- Email input -->
<div class="form-group">
<label for="email">Email Address</label>
<input type="email" onchange={handleFormInput} onblur={validateForm} />
<span lwc:if={errors.email}>{errors.email}</span>
</div>
<!-- Password input -->
<div class="form-group">
<label for="password">Password</label>
<input type="password" onchange={handleFormInput} onblur={validateForm} />
<span lwc:if={errors.password}>{errors.password}</span>
</div>
<!-- Support text for failed logins -->
<div lwc:if={errors.invalidCredentials} class="support">
<span>Not sure what email address you used...</span>
</div>
<!-- reCAPTCHA (conditional) -->
<template lwc:if={captchaOn}>
<c-re-captcha onchecked={handleButtonActivate} onexpired={handleButtonExpire}></c-re-captcha>
</template>
<!-- Submit button -->
<button type="submit" onclick={handleSubmit} disabled={buttonDisabled}>
{isLoading ? 'Logging in...' : 'Login'}
</button>
<!-- Links -->
<a href="/ForgotPassword">Forgot Your Password?</a>
<a href="/SelfRegister">Create an Account</a>
<p>Terms of Use and Privacy Policy...</p>
</form>
</template>
<template lwc:else>
<!-- Logged-in state -->
<div class="login-form bg-light text-center">
<p>{userName}, you are logged in.</p>
<button onclick={handleLogout}>Logout</button>
<!-- Navigation links -->
</div>
</template>
</template>
Key Template Features: - Conditional rendering for guest vs. authenticated states - Dynamic CSS classes for field validation errors - Progressive error display (field-level, then critical textarea) - Conditional reCAPTCHA integration based on failed attempts - Loading state with button text change
JavaScript Controller¶
Properties (API)¶
No public @api properties - Component is self-contained for Experience Cloud pages.
Tracked Properties¶
@track errors¶
- Type:
Object - Purpose: Stores all validation and authentication error messages
- Updated When: Field validation, form submission, login failures
- Structure:
userId¶
- Type:
String - Purpose: Current user's Salesforce ID (from @salesforce/user/Id)
- Updated When: Component initialization
userName¶
- Type:
String - Purpose: Displays user's name when logged in
- Updated When: Wire adapter receives user record data
isGuestUser¶
- Type:
Boolean - Purpose: Determines which template to display (login form vs. logged-in state)
- Updated When: Component initialization (from @salesforce/user/isGuest)
email, password¶
- Type:
String - Purpose: Form field values
- Updated When: User input via handleFormInput
loginCounter¶
- Type:
Number - Purpose: Tracks failed login attempts for current session
- Updated When: Incremented after each failed login, reset on success
recaptchaChecked¶
- Type:
Boolean - Purpose: Tracks whether user completed CAPTCHA challenge
- Updated When: CAPTCHA component fires checked/expired events
isLoading¶
- Type:
Boolean - Purpose: Controls button disabled state and loading text
- Updated When: During async login attempt
Wire Adapters¶
@wire(getRecord, { recordId: '$userId', fields: [NAME_FIELD] })¶
@wire(getRecord, { recordId: '$userId', fields: [NAME_FIELD] })
userRecord({ data }) {
if (data) {
this.userName = data.fields.Name.value;
}
}
Purpose: Retrieves current user's name for display in logged-in state Fields Retrieved: User.Name Error Handling: Silent failure (no error display)
Public Methods¶
None - Component is self-contained
Event Handlers¶
handleFormInput¶
Triggered By: Input fields onchange event Event Type: change Event Detail: event.target.name (field name), event.target.value Action: Updates email or password property
validateForm¶
validateForm() {
// Validates email format
// Validates password requirements
// Sets errors object properties
}
Triggered By: Input fields onblur event Event Type: blur Action: Validates field format and sets error messages
handleSubmit¶
handleSubmit(event) {
event.preventDefault();
// Validates form
// Fetches existing login attempts
// Calls login Apex method
// Handles success/failure
// Updates login attempts
// Redirects on success or max attempts
}
Triggered By: Form submission or button click Event Type: submit, click Action: Orchestrates entire login flow with progressive security
handleButtonActivate¶
Triggered By: c-re-captcha component checked event Action: Sets recaptchaChecked = true, enables submit button
handleButtonExpire¶
Triggered By: c-re-captcha component expired event Action: Sets recaptchaChecked = false, disables submit button
handleLogout¶
Triggered By: Logout button click Action: Redirects to /secur/logout.jsp
Private Methods¶
connectedCallback¶
Purpose: Initializes component, determines redirect URLs, handles PAC contribution workflow flags Called By: LWC lifecycle
isValidForm¶
Purpose: Runs validation and returns boolean for form validity Called By: handleSubmit
fetchExistingLoginAttempts¶
Purpose: Retrieves current login attempt count from server for this username Called By: handleSubmit Returns: Promise
handleUpdateLoginAttempts¶
Purpose: Increments login attempts counter in database after failed login Called By: handleSubmit (after failed login)
handleResetLoginAttempts¶
Purpose: Resets login attempts counter after successful login Called By: handleSubmit (after successful login)
resetForm¶
Purpose: Clears email and password fields Called By: After max attempts or successful login
clearLocalStorage¶
Purpose: Removes 'redirectedFromPAC' flag from localStorage Called By: connectedCallback when PAC redirect detected
Events¶
Events Dispatched¶
None - Component uses standard navigation and form submission
Events Handled¶
checked (from c-re-captcha)¶
Source: c-re-captcha child component
Purpose: User completed CAPTCHA challenge
Handler: handleButtonActivate
expired (from c-re-captcha)¶
Source: c-re-captcha child component
Purpose: CAPTCHA challenge expired
Handler: handleButtonExpire
Styling (CSS)¶
CSS Variables Used¶
None
Custom CSS Classes¶
.login-form: Form container with padding, max-width, centered.btn-submit: Primary button styling with AANP blue brand color (#005999).btn-submit:disabled: Grayed out disabled state (#ccc).btn-submit:hover: Hover state with lighter blue (#3072d7).fs-xxs: Extra small font size (10px).terms: Terms text styling.support: Blue background support message box
SLDS Classes¶
None - Uses Bootstrap-style utility classes (bg-light, form-control, border-danger, etc.)
Responsive Breakpoints¶
- Component uses
:host { display:block; width:100%; }for full-width responsiveness - Form max-width: 600px with horizontal centering
Dependencies¶
Lightning Web Components (Base)¶
None - Uses standard HTML form elements
Custom LWC Components¶
c-re-captcha: reCAPTCHA integration for progressive security
Apex Classes¶
SelfRegisterController.login(): Authenticates user credentialsSelfRegisterController.getCurrentUserLoginAttempts(): Retrieves login attempt countSelfRegisterController.updateLoginAttempts(): Increments login attemptsSelfRegisterController.resetLoginAttempts(): Resets attempts after success
Salesforce Objects & Fields¶
User: Name field for display- Custom object/storage for login attempts tracking (implementation in Apex)
Static Resources¶
None
Labels¶
None - All text is hardcoded
External Services¶
- reCAPTCHA service (via c-re-captcha component)
Configuration¶
Component Meta XML¶
<targets>
<target>lightningCommunity__Page</target>
<target>lightningCommunity__Default</target>
</targets>
Available On: - Experience Cloud Site pages (standard pages and custom pages)
Design Properties¶
None - Component has no configurable properties
User Interactions¶
Actions Available to Users¶
- Enter Credentials: Type email and password
- Trigger: User input in form fields
-
Result: Real-time validation on blur
-
Submit Login: Attempt authentication
- Trigger: Click Login button or press Enter
-
Result: Validates, calls Apex, redirects on success
-
Complete CAPTCHA: Solve reCAPTCHA challenge (after 2 failed attempts)
- Trigger: CAPTCHA appears after threshold
-
Result: Enables submit button when solved
-
Reset Password: Navigate to forgot password flow
- Trigger: Click "Forgot Your Password?" link
-
Result: Redirects to /ForgotPassword
-
Create Account: Navigate to self-registration
- Trigger: Click "Create an Account" link
-
Result: Redirects to /SelfRegister
-
Logout: End authenticated session
- Trigger: Click Logout button (when logged in)
- Result: Redirects to /secur/logout.jsp
Validation & Error Handling¶
Client-Side Validation:
- Email: Required, valid email format (regex: /^[^\s@]+@[^\s@]+\.[^\s@]+$/)
- Password: Required, minimum 8 characters, 1 uppercase, 1 lowercase, 1 number (regex: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/)
Error Messages: - "Email is required" - "Invalid email address" - "Password is required" - "Password must be at least 8 characters, and contain at least 1 uppercase, 1 lowercase, and 1 number." - "The credentials you entered are not correct. Please check your email address and password and try again." - "The email/password combination was incorrect. You have 1 more attempt before your account will be locked."
Loading States: - Button text changes to "Logging in..." during authentication - Button disabled during loading and when CAPTCHA required but not completed
Progressive Security: - After 0 failed attempts (configured): CAPTCHA appears (currently disabled - CAPTCHA_THRESHOLD = 0) - After 4 failed attempts (MAXIMUM_ATTEMPTS = 4): Automatic redirect to forgot password page - Login attempt counter stored server-side per username
Data Flow¶
Input Data Flow¶
User enters credentials
↓
handleFormInput updates properties
↓
validateForm checks format
↓
handleSubmit validates and calls Apex
↓
fetchExistingLoginAttempts retrieves count
↓
login() authenticates with Salesforce
Output Data Flow (Success)¶
Login successful
↓
handleResetLoginAttempts clears counter
↓
window.location.href = redirect URL
↓
User navigated to destination (home or PAC contribution)
Output Data Flow (Failure)¶
Login failed
↓
handleUpdateLoginAttempts increments counter
↓
Check if max attempts reached
↓
If max: Redirect to forgot password
↓
If not max: Display error, show CAPTCHA if threshold reached
Performance Considerations¶
Render Optimization: - Conditional rendering reduces DOM (guest vs. logged-in states) - Dynamic CSS classes avoid unnecessary re-renders - Getters for computed properties
Data Volume: - Minimal data transfer (single user record) - Login attempts stored server-side
API Call Optimization: - Sequential async calls in handleSubmit (fetch attempts → login → update attempts) - No caching needed (authentication should not be cached)
Accessibility (a11y)¶
ARIA Labels: - Form labels properly associated with inputs via for/id attributes - Error spans lack aria-describedby associations - No aria-live regions for dynamic error messages
Keyboard Navigation: - Tab order: Natural progression through form - Enter key submits form - All links accessible via keyboard
Screen Reader Support: - Labels read correctly - Error messages read when present but not announced dynamically - Support text provides helpful context - No screen reader-only text for important state changes
Color Contrast: - Blue brand color (#005999) on white: Good contrast - Red error styling: Standard Bootstrap danger colors - Disabled button gray (#ccc): May not meet WCAG AA contrast
Testing¶
Jest Tests¶
Test File: Not found in source Coverage: 0%
Test Scenarios Needed: - Component renders in guest mode - Component renders in logged-in mode - Email validation works correctly - Password validation works correctly - Form validation prevents submission with errors - handleSubmit calls Apex correctly - Failed login increments counter - CAPTCHA appears after threshold - Max attempts redirect to forgot password - Successful login resets counter - PAC redirect logic works correctly - Logout redirects correctly - Error messages display correctly
Manual Testing Checklist¶
- [ ] Desktop browser (Chrome, Firefox, Safari)
- [ ] Mobile browser (iOS Safari, Android Chrome)
- [ ] Tablet view
- [ ] Valid credentials login successfully
- [ ] Invalid credentials show error
- [ ] CAPTCHA appears and functions correctly
- [ ] Max attempts lock-out works
- [ ] PAC contribution redirect works
- [ ] Forgot password link works
- [ ] Create account link works
- [ ] Logout works
- [ ] Screen reader announces errors
- [ ] Keyboard-only navigation works
- [ ] Terms links open correctly
Usage Examples¶
In App Builder¶
Not applicable - Experience Cloud component
In Experience Builder¶
- Drag component onto Login page
- No configuration needed
- Publish site
In Code¶
Component is self-contained and not designed for programmatic use.
Changes & History¶
No version history available in source code.
Notable TODOs in code: - Line 111: "TODO: Validate by each field" - Current validation validates all fields at once - Line 251: "TODO: Find a way to rework getters for dynamic classes" - CSS class getters could be optimized
Pre-Go-Live Concerns¶
CRITICAL - Fix Before Go-Live¶
- Security Vulnerability: Login attempt counter stored client-side (
this.loginCounter) can be manipulated by users in browser console - Hardcoded Portal Path: Multiple references to 'aanpstoredc' limit portability to other Experience Cloud sites
- CAPTCHA Disabled: CAPTCHA_THRESHOLD = 0 and MAXIMUM_ATTEMPTS = 4 mean CAPTCHA never appears (security risk)
- LocalStorage Dependency: PAC redirect logic relies on browser localStorage which can be cleared, causing workflow breaks
- Missing ARIA Associations: Error messages not connected to fields via aria-describedby
HIGH - Address Soon After Go-Live¶
- Error Message Exposure: Generic error messages don't distinguish between "user doesn't exist" and "wrong password" (security vs. usability tradeoff)
- No Password Reset Token: After max attempts, user redirected but no token passed to forgot password page
- Console Logging: Debug statements (lines 201, 204, 211, 222, 248) left in production code
- Incomplete Validation: Email validation regex allows some invalid formats, password validation doesn't check for special characters
- Hard Refresh on Redirect:
window.location.hrefforces full page reload instead of SPA navigation
MEDIUM - Future Enhancement¶
- Code Duplication: URL construction logic repeated multiple times (lines 54-96)
- Magic Numbers: CAPTCHA_THRESHOLD and MAXIMUM_ATTEMPTS should be Custom Settings or metadata
- TODO Comments: Incomplete implementations noted in code
- Limited Error Context: User has no guidance on what to do after account lockout
- No "Remember Me" Option: Users must log in every session
LOW - Monitor¶
- Commented Code: Lines 6-10, 269-271 contain commented-out code that should be removed
- Variable Naming: Some inconsistencies (hadleSubmit vs handleSubmit typo line 3)
- CSS Comments: Commented CSS at top of file
- Button Width: min-w-{200} looks like Tailwind syntax but may not work
Maintenance Notes¶
Complexity: Medium - Standard login form with progressive security Recommended Review Schedule: Quarterly during security reviews
Key Maintainer Notes: - This is the primary authentication entry point - any changes affect all users - Login attempt tracking is split between client-side counter and server-side storage - CAPTCHA integration requires active reCAPTCHA site key configuration - PAC contribution workflow uses localStorage flags set by other components - Portal path detection logic assumes specific URL structure (/aanpstoredc/login or /login) - Failed login security measures can be adjusted via CAPTCHA_THRESHOLD and MAXIMUM_ATTEMPTS constants - Component automatically detects guest vs. authenticated state using @salesforce/user/isGuest - Forgot password redirect is automatic after max attempts (no user confirmation)
Browser Compatibility: - Chrome: Latest - Firefox: Latest - Safari: Latest - Mobile: iOS 12+, Android 8+
Dependencies to Monitor: - Salesforce Site authentication service changes - reCAPTCHA API version updates - SelfRegisterController Apex class changes - Experience Cloud authentication flow changes