Skip to content

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 states
  • loginForm.js - JavaScript controller with authentication logic and progressive security
  • loginForm.css - Styling for form and buttons
  • loginForm.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:
    {
        email: 'Error message',
        password: 'Error message',
        invalidCredentials: 'Login failure message'
    }
    

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

handleFormInput(event) {
    const field = event.target.name;
    this[field] = event.target.value;
}

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 credentials
  • SelfRegisterController.getCurrentUserLoginAttempts(): Retrieves login attempt count
  • SelfRegisterController.updateLoginAttempts(): Increments login attempts
  • SelfRegisterController.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

  1. Enter Credentials: Type email and password
  2. Trigger: User input in form fields
  3. Result: Real-time validation on blur

  4. Submit Login: Attempt authentication

  5. Trigger: Click Login button or press Enter
  6. Result: Validates, calls Apex, redirects on success

  7. Complete CAPTCHA: Solve reCAPTCHA challenge (after 2 failed attempts)

  8. Trigger: CAPTCHA appears after threshold
  9. Result: Enables submit button when solved

  10. Reset Password: Navigate to forgot password flow

  11. Trigger: Click "Forgot Your Password?" link
  12. Result: Redirects to /ForgotPassword

  13. Create Account: Navigate to self-registration

  14. Trigger: Click "Create an Account" link
  15. Result: Redirects to /SelfRegister

  16. Logout: End authenticated session

  17. Trigger: Click Logout button (when logged in)
  18. 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

  1. Drag component onto Login page
  2. No configuration needed
  3. 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.href forces 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