Custom Keycloak authenticator for email verification during login flows with support for personal email password reset functionality. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Keycloak Email Verification Authenticator
Custom Keycloak authenticator extension for password reset with email verification challenge. Designed for environments where users have a separate personal recovery email address stored as a user attribute.
Features
- Email Verification Challenge: Displays masked recovery email (e.g.,
j***e@g***l.com) and requires user to enter complete address - Rate Limiting: Maximum 3 verification attempts per session to prevent brute force
- Personal Email Support: Sends password reset to
personal_emailattribute instead of primary email - Security Focused: Prevents automated password reset spam and account enumeration
- Professional UI: Clean, responsive templates with dark mode support
- i18n Ready: Full internationalization support via message properties
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Password Reset Flow │
├─────────────────────────────────────────────────────────────────┤
│ │
│ User clicks "Forgot Password?" │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Choose User │ (Enter username/email) │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ Email Challenge │ ──▶ │ Shows: j***e@g***l.com │ │
│ │ Authenticator │ │ User enters full email │ │
│ └────────┬────────┘ │ 3 attempts max │ │
│ │ └─────────────────────────┘ │
│ ▼ │
│ ┌─────────────────┐ ┌─────────────────────────┐ │
│ │ Send Reset │ ──▶ │ Email sent to │ │
│ │ Email Personal │ │ personal_email attr │ │
│ └────────┬────────┘ └─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Reset Password │ (User clicks link, sets new password) │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Requirements
- Keycloak: 25.0.6 or later
- Java: 17 or later
- Maven: 3.8 or later
- Docker: For local development (optional)
Quick Start
1. Build
mvn clean package
2. Local Development with Docker
# Start Keycloak + PostgreSQL + MailHog
docker-compose up -d
# Wait for startup (~60 seconds)
docker-compose logs -f keycloak
# Access Keycloak Admin Console
open http://localhost:8080/admin
# Login: admin / admin
# Access MailHog (view sent emails)
open http://localhost:8025
3. Test Password Reset
- Go to: http://localhost:8080/realms/test-realm/account
- Click "Sign out" if logged in
- Click "Forgot Password?"
- Enter:
testuser - See masked email:
t***r@g***l.com - Enter:
testuser.personal@gmail.com - Check MailHog for reset email
- Click link and set new password
Production Deployment
See DEPLOYMENT.md for detailed production deployment instructions.
Quick Deploy
# 1. Build
mvn clean package
# 2. Copy JAR to Keycloak
cp target/keycloak-email-verification-authenticator-1.0.0.jar /opt/keycloak/providers/
# 3. Copy theme
cp -r src/main/resources/theme-resources/thompsonhall-theme /opt/keycloak/themes/
# 4. Restart Keycloak
systemctl restart keycloak
Configuration
User Attribute
Users must have a personal_email attribute set:
Via Admin Console:
- Users → Select User → Attributes
- Add: Key =
personal_email, Value =user@personal-domain.com
Via User Profile:
- Realm Settings → User Profile
- Add attribute:
personal_email
Authentication Flow
Create a custom reset credentials flow:
-
Authentication → Create Flow
-
Name:
reset-credentials-with-verification -
Add executions:
- Choose User (REQUIRED)
- Email Verification Challenge (REQUIRED)
- Send Reset Email to Personal Address (REQUIRED)
- Reset Password (REQUIRED)
-
Bind flow:
- Authentication → Bindings
- Reset credentials flow →
reset-credentials-with-verification
Theme
Set the custom theme:
- Realm Settings → Themes
- Login Theme:
thompsonhall-theme
SMTP
Configure email server:
- Realm Settings → Email
- Set Host, Port, From address
- Test connection
Project Structure
keycloak-email-verification-authenticator/
├── pom.xml # Maven configuration
├── docker-compose.yml # Local development environment
├── README.md # This file
├── DEPLOYMENT.md # Production deployment guide
├── docker/
│ └── realm-export.json # Test realm configuration
└── src/
├── main/
│ ├── java/com/thompsonhall/keycloak/
│ │ ├── authenticators/
│ │ │ ├── EmailVerificationAuthenticator.java
│ │ │ ├── EmailVerificationAuthenticatorFactory.java
│ │ │ ├── SendResetEmailToPersonal.java
│ │ │ └── SendResetEmailToPersonalFactory.java
│ │ └── utils/
│ │ └── EmailMaskingUtil.java
│ └── resources/
│ ├── META-INF/services/
│ │ └── org.keycloak.authentication.AuthenticatorFactory
│ └── theme-resources/thompsonhall-theme/login/
│ ├── theme.properties
│ ├── email-verification-challenge.ftl
│ ├── reset-email-sent.ftl
│ ├── messages/
│ │ └── messages_en.properties
│ └── resources/css/
│ ├── email-verification.css
│ └── password-reset.css
└── test/
└── java/com/thompsonhall/keycloak/utils/
└── EmailMaskingUtilTest.java
Security Features
- Email Masking: Prevents disclosure of recovery email address
- Rate Limiting: 3 attempts max prevents brute force
- Session-based Tracking: Attempts reset on new session
- Audit Logging: All verification attempts logged
- XSS Protection: All templates use proper escaping
- Generic Errors: Prevents account enumeration
Customization
Email Masking
Modify EmailMaskingUtil.java to change masking behavior:
MAX_MASK_LENGTH: Maximum asterisks (default: 3)MASK_CHAR: Masking character (default:*)
Attempt Limit
Modify EmailVerificationAuthenticator.java:
private static final int MAX_ATTEMPTS = 3; // Change as needed
Styling
Edit CSS files in resources/css/:
email-verification.css: Verification challenge pagepassword-reset.css: Email sent confirmation page
Messages
Edit messages_en.properties for text changes. Add additional language files (e.g., messages_fr.properties) for i18n.
Troubleshooting
Authenticator Not Appearing
# Check JAR deployed
ls -la /opt/keycloak/providers/*.jar
# Check Keycloak logs for SPI registration
grep "email-verification-challenge" /opt/keycloak/data/log/keycloak.log
Theme Not Loading
# Check theme files
ls -la /opt/keycloak/themes/thompsonhall-theme/
# Clear theme cache
rm -rf /opt/keycloak/data/tmp/kc-gzip-cache
# Restart Keycloak
systemctl restart keycloak
User Missing personal_email
Check user attributes:
- Admin Console → Users → Select User → Attributes
- Verify
personal_emailexists and has value
Development
Run Tests
mvn test
Security Scan
mvn dependency-check:check
Hot Reload (Theme Changes)
Theme changes can be seen without restart - just clear browser cache and refresh.
Code Changes
Rebuild and restart Keycloak:
mvn clean package
docker-compose restart keycloak
License
MIT License - see LICENSE file.
Contributing
- Fork the repository
- Create a feature branch
- Make changes with tests
- Submit a pull request
Support
For issues or questions:
- Open a GitHub issue
- Contact: support@thompsonhall.digital