Login Flow
1. What does this feature do? (High-Level Overview)
Section titled “1. What does this feature do? (High-Level Overview)”The Login Flow handles user authentication through multiple pathways: standard email/phone login, two-factor authentication verification, and location selection for employees. It supports flexible identifier input (email or phone number) with automatic detection and normalization.
2. Who is this for? (Roles & Permissions)
Section titled “2. Who is this for? (Roles & Permissions)”- All Users: Any registered user (employees or parents) can authenticate using their credentials.
- Employees: Required to select a location after successful authentication.
- Parents: Receive immediate access without location selection.
- No special permissions required for login (public endpoint).
3. Business Rules & Enforcements
Section titled “3. Business Rules & Enforcements”- Rule 1: User status must be ACTIVE. BLACK_LIST status results in 403 error.
- Rule 2: Phone numbers are normalized to last 10 digits (US format).
- Rule 3: Email addresses are case-sensitive (no transformation).
- Rule 4: If 2FA is enabled for user, authentication requires code verification.
- Rule 5: Employees must select a location with active access (via
user_locationstable). - Rule 6: Admin View requires
access_admin_viewglobal permission. - Rule 7: Location must be ACTIVE or STOP status.
- Rule 8: ALL users (including SUPERADMIN) must have explicit location access - no bypass.
4. UI Placement
Section titled “4. UI Placement”Login Page: /login
5. How-To Guide (Step-by-Step)
Section titled “5. How-To Guide (Step-by-Step)”Scenario A: Standard Employee Login (Without 2FA)
Section titled “Scenario A: Standard Employee Login (Without 2FA)”Step 1: Navigate to Login Page
- Open application at
/login - Login form displays with identifier and password fields
Step 2: Enter Credentials
Identifier: user@example.comPassword: ********Identifier can be:
- Email address:
john.doe@example.com - Phone number:
(555) 123-4567or5551234567or+1 555-123-4567
Step 3: Submit Login Request
Step 4: System Processing
- Auto-detects identifier type (EMAIL or PHONE)
- Normalizes identifier:
- Email: Used as-is (case-sensitive)
- Phone: Extracts last 10 digits →
5551234567
- Attempts JWT authentication
- Validates user status (ACTIVE required)
- Checks 2FA requirement (FALSE in this scenario)
Step 5: Response - Location Selection Required
Step 6: Select Location
Location Selection Dialog displays with available locations.
Step 7: System Validation
- Validates JWT token
- Checks user has access to location via
user_locationspivot - Validates location status (ACTIVE or STOP)
- Sets Spatie Permission context for location
- Creates AuthSession with refresh token
- Records LoginSession for audit
Step 8: Final Response - Authentication Complete
Step 9: Client Storage
- Store
access_tokenin memory or localStorage - Store
refresh_tokensecurely (HttpOnly cookie or secure storage) - Store user object
- Redirect to dashboard
Scenario B: Employee Login with Two-Factor Authentication
Section titled “Scenario B: Employee Login with Two-Factor Authentication”Steps 1-4: Same as Scenario A
Step 5: Response - 2FA Required
Step 6: Select Verification Method
2FA Method Selection Dialog displays:
- Option 1: SMS to 55****67
- Option 2: Email to u***@example.com
User selects: Email
Step 7: Request Verification Code
Step 8: System Processing
- Finds user by email
- Validates email format
- Resets verification attempts to 0
- Clears rate limit for fresh start
- Generates 6-digit code
- Hashes code with bcrypt
- Stores with 10-minute expiration (UTC)
- Sends code via email
Step 9: Code Sent Response
User receives email with subject: “Your PMC App Security Code”
Your verification code is: 123456
This code will expire in 10 minutes.
If you didn't request this code, please ignore this email.Step 10: Enter Verification Code
2FA Verification Page displays:
- 6-digit code input
- Resend code button
- Use backup code link
- Time remaining indicator
Step 11: Code Verification
- Finds user by email
- Checks code expiration (auto-cleanup if expired)
- Verifies code using
Hash::check()(constant-time) - Increments attempts on failure
- Clears 2FA fields on success
- Generates JWT token
- Retrieves locations for selection
- Records login session
Step 12: Verification Success - Location Selection Required
Steps 13-15: Same as Scenario A Steps 6-9 (Location selection and final response)
Scenario C: Parent Login with 2FA
Section titled “Scenario C: Parent Login with 2FA”Step 1: Navigate to Login Page
- Opens application at
/login - May have separate parent login form or unified form
Step 2: Enter Credentials
Identifier: parent@example.comPassword: ********Step 3: Submit Login Request
Step 4: System Processing
- Uses
apiparentguard - Auto-detects and normalizes identifier
- Attempts JWT authentication
- Validates parent status (ACTIVE, not BLACK_LIST)
- Checks 2FA requirement (TRUE in this scenario)
Step 5: Response - 2FA Method Selection
Steps 6-9: Same as Employee 2FA (Method selection and code sending)
Step 10: Enter Verification Code
Step 11: Verification Success - Immediate Access
Key Difference: Parents receive tokens immediately without location selection.
Scenario D: Admin View Login
Section titled “Scenario D: Admin View Login”Admin View: Special location providing cross-location visibility.
Steps 1-4: Standard Employee Login
Step 5: Location Selection Dialog
Available locations include:
- Regular locations: Miami Clinic, Orlando Clinic
- Admin View (if user has
access_admin_viewpermission)
Step 6: Select Admin View
Step 7: Validation
- Checks user has
access_admin_viewglobal permission - Sets admin view context
- Loads all-locations permissions
Step 8: Response
6. What happens if…? (Edge Cases / FAQ)
Section titled “6. What happens if…? (Edge Cases / FAQ)”-
Q: What happens if I enter an invalid email format?
- A: The system detects it as a PHONE identifier and tries phone-based lookup. If not found, returns “Invalid credentials.”
-
Q: Can I login with partial phone numbers?
- A: The system normalizes all phone inputs to the last 10 digits. Formats like
555-1234,(555) 123-4567, or+1 555-123-4567all become5551234567.
- A: The system normalizes all phone inputs to the last 10 digits. Formats like
-
Q: What happens if 2FA code expires while I’m on the verification page?
- A: When you submit the expired code, the system auto-cleans it and returns:
{"error": "Verification code has expired", "code_expired": true}. Click “Resend Code” to get a new one.
- A: When you submit the expired code, the system auto-cleans it and returns:
-
Q: What happens after 5 failed verification attempts?
- A: The code is automatically cleared and you must request a new one. Returns:
{"error": "Maximum attempts exceeded", "attempts_remaining": 0}.
- A: The code is automatically cleared and you must request a new one. Returns:
-
Q: What if I don’t have access to any locations?
- A: Authentication succeeds but location selection shows empty list. Contact your administrator to grant location access via
user_locationstable.
- A: Authentication succeeds but location selection shows empty list. Contact your administrator to grant location access via
-
Q: Can SUPERADMIN bypass location selection?
- A: No, ALL users must have explicit location access. SUPERADMIN must have
user_locationsentries just like any other user.
- A: No, ALL users must have explicit location access. SUPERADMIN must have
-
Q: What happens if I select an inactive location?
- A: Location selection validates status. Only ACTIVE or STOP locations are allowed. Inactive locations are hidden from the list.
-
Q: Can I switch locations after logging in?
- A: Yes, use the location switcher in the header or call
POST /api/auth/change-locationwith a newlocation_id. This requires re-authentication with JWT.
- A: Yes, use the location switcher in the header or call
-
Q: What happens if the location I’m using becomes inactive during my session?
- A: Your current session continues until token expiration. However, you cannot refresh the token or switch back to that location.
Development Mode
Section titled “Development Mode”2FA Bypass for Testing:
When it is not on AND TWO_FACTOR_ENABLED=true:
- Any 6-digit code is accepted for LOGIN context
- Real codes still required for PASSWORD_RESET context
- Codes logged to Laravel log for debugging
- SMS not sent (saves cost)
Example Log:
[2026-03-31 10:00:00] local.INFO: 2FA Development Mode: Generated code 123456 for user user@example.comSecurity Considerations
Section titled “Security Considerations”Identifier Normalization:
- Phone: Last 10 digits only (US format)
- Email: Case-sensitive, no transformation
- Prevents timing attacks (same processing time)
Password Security:
- Bcrypt hashing (cost factor from config)
- Constant-time comparison via
Hash::check() - No password in responses or logs
Session Tracking:
- Every login recorded in
login_sessionstable - IP address, device, browser, OS captured
- Audit trail for security monitoring
Error Messages:
- Generic “Invalid credentials” for wrong email/password
- No indication if email exists or not
- Prevents user enumeration attacks
Rate Limiting:
- Applied to 2FA endpoints
- Prevents brute-force attacks
- Configurable thresholds