Kyle Thompson 12f2e45a4b Initial commit: Keycloak email verification authenticator
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>
2025-12-01 21:11:32 -04:00

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_email attribute 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

  1. Go to: http://localhost:8080/realms/test-realm/account
  2. Click "Sign out" if logged in
  3. Click "Forgot Password?"
  4. Enter: testuser
  5. See masked email: t***r@g***l.com
  6. Enter: testuser.personal@gmail.com
  7. Check MailHog for reset email
  8. 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:

  1. Users → Select User → Attributes
  2. Add: Key = personal_email, Value = user@personal-domain.com

Via User Profile:

  1. Realm Settings → User Profile
  2. Add attribute: personal_email

Authentication Flow

Create a custom reset credentials flow:

  1. Authentication → Create Flow

  2. Name: reset-credentials-with-verification

  3. Add executions:

    • Choose User (REQUIRED)
    • Email Verification Challenge (REQUIRED)
    • Send Reset Email to Personal Address (REQUIRED)
    • Reset Password (REQUIRED)
  4. Bind flow:

    • Authentication → Bindings
    • Reset credentials flow → reset-credentials-with-verification

Theme

Set the custom theme:

  1. Realm Settings → Themes
  2. Login Theme: thompsonhall-theme

SMTP

Configure email server:

  1. Realm Settings → Email
  2. Set Host, Port, From address
  3. 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 page
  • password-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:

  1. Admin Console → Users → Select User → Attributes
  2. Verify personal_email exists 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

  1. Fork the repository
  2. Create a feature branch
  3. Make changes with tests
  4. Submit a pull request

Support

For issues or questions:

Description
Custom Keycloak authenticator for email verification during login flows
Readme 48 KiB
Languages
Java 66%
CSS 19.4%
Fluent 14.6%