Google Login Automation¶
This document describes the technical implementation of Google OAuth authentication for the Spokane Mountaineers Experience Cloud site.
Access¶
- Apex Class:
force-app/main/default/classes/GoogleAuthRegistrationHandler.cls - Test Class:
force-app/main/default/classes/GoogleAuthRegistrationHandlerTest.cls - Auth Provider: Setup → Identity → Auth. Providers → Google
Purpose¶
The GoogleAuthRegistrationHandler implements Salesforce's Auth.RegistrationHandler interface to manage user authentication for Google OAuth login on Experience Cloud sites. It:
- Authenticates existing users via Google OAuth
- Matches users by username pattern (
email + '.smi') - Prevents new account creation (Donorbox-only registration)
- Provides actionable error messages for users
Configuration¶
Prerequisites¶
- My Domain must be deployed
- Google Auth Provider must be configured with:
- Consumer Key and Secret from Google Cloud Console
- Scopes:
openid email profile - Registration Handler:
GoogleAuthRegistrationHandler
- Experience Cloud Site must have:
- Google Auth Provider enabled in Login & Registration settings
- Social Login component added to the Login page
- Self-Registration disabled (to prevent bypassing Donorbox)
Setup Steps¶
-
Configure Google Auth Provider:
- Navigate to: Setup → Identity → Auth. Providers → Google
- Assign Registration Handler:
GoogleAuthRegistrationHandler - Set "Execute Registration As" to a user with appropriate permissions
-
Enable for Experience Cloud Site:
- Navigate to: Setup → Digital Experiences → All Sites → Spokane Mountaineers → Administration → Login & Registration
- Under Authentication Providers, enable Google
- Under Registration, set Self-Registration to Disabled
-
Add Social Login Component:
- Open Experience Builder for your site
- Navigate to the Login page
- Add the Social Login component
- Publish changes
Implementation Details¶
User Matching Strategy¶
The handler uses username pattern matching for reliable user identification:
String usernamePattern = email.toLowerCase().trim() + '.smi';
List<User> usersByUsername = [
SELECT Id, Username, Email, IsActive
FROM User
WHERE Username = :usernamePattern
LIMIT 1
];
Why username pattern matching?
- Usernames cannot be modified - Provides a stable identifier for authentication
- Email addresses can change - Members might update their email in Salesforce, but username remains constant
- Single query - Efficient database lookup using indexed Username field
- Predictable format - Always follows
email@example.com.smipattern
Account Creation Prevention¶
The handler does not create new accounts. If no matching user is found, it throws an exception with actionable guidance:
if (!usersByUsername.isEmpty()) {
return usersByUsername[0];
}
// No user found - provide actionable guidance
throw new RegistrationHandlerException(
'No account found for this email address.\n\n' +
'If you are a new member, please sign up at:\n' +
'https://donorbox.org/spokanemountaineers-membership-2\n\n' +
'If you already have an account, please:\n' +
'1. Use your usual login method for now\n' +
'2. Report this issue to webdev@spokanemountaineers.org\n\n' +
'We will help you link your Google account to your existing membership.'
);
This ensures that:
- New memberships go through Donorbox (payment required)
- Existing members get clear guidance on next steps
- No duplicate accounts are created
Flow Diagram¶
Source: google-login-flow.d2
Code Structure¶
Main Methods¶
createUser(Id portalId, Auth.UserData data)¶
Main entry point called by Salesforce during Google OAuth flow.
Parameters:
portalId: Network (Experience Cloud site) IDdata: User data from Google OAuth callback (includes email, firstName, lastName)
Returns: User record if found, throws exception if not found
Logic:
- Validates email is present
- Calls
findExistingUser()to locate user - If found, returns user
- If not found, throws exception with actionable guidance
findExistingUser(String email)¶
Finds existing user by username pattern.
Parameters:
email: Email address from Google OAuth
Returns: User record if found, null if not found
Logic:
- Constructs username pattern:
email.toLowerCase().trim() + '.smi' - Queries User table for matching username
- Returns first match or
null
updateUser(Id userId, Id portalId, Auth.UserData data)¶
Called when user data needs updating (not used in this implementation).
Note: This method intentionally does nothing. Existing flows handle user updates automatically.
Testing¶
Test Coverage¶
The test class GoogleAuthRegistrationHandlerTest provides 100% code coverage with 22 test methods covering:
- User matching by username pattern
- Error handling for blank emails
- Error handling for non-existent users
- Helper method testing (alias generation, password generation, etc.)
Running Tests¶
sf apex run test --class-names GoogleAuthRegistrationHandlerTest --target-org smi --code-coverage
Test Scenarios¶
- Existing User Found: Verifies successful login when username pattern matches
- User Not Found: Verifies exception is thrown with actionable error message
- Blank Email: Verifies exception is thrown for invalid input
- Edge Cases: Tests helper methods with various inputs
Error Handling¶
Common Error Scenarios¶
No Account Found¶
User sees:
No account found for this email address.
If you are a new member, please sign up at:
https://donorbox.org/spokanemountaineers-membership-2
If you already have an account, please:
1. Use your usual login method for now
2. Report this issue to webdev@spokanemountaineers.org
We will help you link your Google account to your existing membership.
Resolution:
- New members: Direct to Donorbox signup
- Existing members: Contact webdev for account linking assistance
Blank Email¶
User sees:
Email is required for authentication
Resolution: This should not occur in normal operation. If it does, contact webdev.
Maintenance¶
Updating Username Pattern¶
If the .smi suffix is removed in the future, update line 96 in GoogleAuthRegistrationHandler.cls:
Current:
String usernamePattern = email.toLowerCase().trim() + '.smi';
Future (if .smi removed):
String usernamePattern = email.toLowerCase().trim();
Adding Additional Auth Providers¶
To add support for additional OAuth providers (e.g., Microsoft, Facebook):
- Create new Auth Provider in Setup
- Create new Registration Handler class (or extend existing)
- Assign handler to Auth Provider
- Enable provider in Experience Cloud site settings
Monitoring¶
Monitor for:
- Failed login attempts (check debug logs)
- User-reported issues (webdev@spokanemountaineers.org)
- Exception frequency (Salesforce debug logs)
Performance¶
Query Optimization¶
- Single SOQL query per login attempt
- Indexed field (Username) for fast lookups
- LIMIT 1 to minimize data transfer
- Efficient database lookup with minimal overhead
Security Considerations¶
Account Creation Prevention¶
- Self-Registration must be disabled in Experience Cloud settings
- Handler explicitly throws exceptions instead of creating users
- Donorbox remains the only account creation path
User Matching¶
- Username pattern matching provides a stable authentication identifier
- Usernames cannot be modified, preventing account hijacking
- Email changes don't break authentication since matching is based on username
Error Messages¶
- Error messages are user-friendly but don't expose system internals
- Clear guidance directs users to appropriate support channels
Related Documentation¶
- Sign in with Google (member guide) - User-friendly overview
- Experience Cloud Setup - Salesforce documentation
- Auth.RegistrationHandler Interface - Salesforce Apex documentation
Troubleshooting¶
Google Button Not Appearing¶
- Verify Auth Provider is enabled for the Experience Cloud site
- Check that Social Login component is added to Login page
- Verify Self-Registration settings (should be disabled)
- Check browser console for JavaScript errors
Login Fails for Existing User¶
- Verify user's username follows pattern:
email@example.com.smi - Check that user is active (
IsActive = true) - Review debug logs for exception details
- Verify email from Google matches username pattern
Exception Messages Not Showing¶
- Check Salesforce debug logs for full exception details
- Verify error handling in Experience Cloud login component
- Test with different browsers/devices
Support¶
For issues or questions about Google login automation: