Understanding the Difference Between Register, Login, and Password Recovery Methods in Holehe Modules

Holehe modules use three distinct interaction patterns—register, login, and password recovery—to verify email existence across 150+ services without exposing sensitive credentials.

The Holehe OSINT tool (megadose/holehe) identifies whether an email address is registered on various platforms by exploiting public-facing endpoints. Each module declares a method constant that dictates its behavior: attempting account creation, authentication, or password reset flows. The core engine dispatches these methods uniformly, while individual modules handle service-specific nuances.


How Module Methods Work in Holehe

Holehe's architecture separates orchestration from implementation. The core engine (holehe/core.py) loads modules dynamically, inspects their method attribute, and executes their check() function. The module itself contains all HTTP logic, response parsing, and error handling. This design allows contributors to add new services without modifying core code.

All three methods follow an identical async signature:

async def check(email: str, client: httpx.AsyncClient) -> dict:
    # Module-specific HTTP request and response analysis

    return {"email": email, "status": "...", "method": "..."}

The method constant (defined at module level) informs the core how results should be categorized and reported.


The Three Method Types Explained

Register Method

Purpose: Detect existing accounts by attempting to create a new one with the target email.

Register modules exploit a common API pattern: services reject registration when an email is already in use. The module sends a registration request and interprets "email already taken" responses as confirmation of account existence.

Typical workflow:

  1. Build registration payload (email, username placeholder, password).
  2. POST to the service's signup endpoint.
  3. Parse response for existence indicators like "already registered" or HTTP 409.

Advantage: No valid credentials required; purely passive detection.


Login Method

Purpose: Confirm account existence through authentication endpoints.

Login modules submit the target email with an intentionally invalid password. Services respond differently for "email not found" versus "password incorrect"—the latter confirms the email exists. Some modules also detect successful session establishment for accounts without password protection or with leaked credentials.

Typical workflow:

  1. Construct login request with email and dummy password.
  2. POST to authentication endpoint.
  3. Analyze error messages, redirects, or session cookies.

Advantage: Can distinguish between non-existent accounts and wrong-password scenarios, providing higher confidence in results.


Password Recovery Method

Purpose: Trigger "forgot password" flows to verify email registration.

Password recovery modules interact with publicly accessible reset mechanisms. Services typically respond with generic confirmations like "reset email sent" or return masked versions of the registered email (e.g., t***@gmail.com). Either response confirms account existence without requiring authentication.

Typical workflow:

  1. Submit email to password reset endpoint.
  2. Parse JSON or HTML response for success indicators.
  3. Extract masked email or confirmation message when available.

Advantage: Generally lower rate-limiting and blocking risk than login attempts; legally safer as it uses intended user workflows.


Code Examples from the Holehe Repository

Register Module: Twitter

File: holehe/modules/social_media/twitter.py

method = "register"

async def check(email: str, client):
    headers = {
        "User-Agent": "Mozilla/5.0",
        "Authorization": "Bearer AAAAAAAAAAAAAAAAAAAA...",
        "Content-Type": "application/json"
    }
    data = {
        "email": email,
        "flow_name": "signup",
        "flow_data": {"requested_variant": "email"}
    }
    resp = await client.post(
        "https://api.twitter.com/1.1/users/email_available.json",
        headers=headers,
        json=data
    )
    result = resp.json()
    if not result.get("valid", False) and result.get("taken", False):
        return {
            "email": email,
            "status": "registered",
            "exists": True,
            "method": "register"
        }
    return {"email": email, "status": "available", "exists": False}

The Twitter module queries an internal API endpoint that validates email availability during signup. A taken: true response definitively confirms account existence.


Login Module: Facebook

File: holehe/modules/social_media/facebook.py

method = "login"

async def check(email: str, client):
    headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
    data = {
        "email": email,
        "pass": "INVALID_PASSWORD_FOR_ENUMERATION",
        "login": "1"
    }
    resp = await client.post(
        "https://www.facebook.com/login.php",
        headers=headers,
        data=data,
        allow_redirects=False
    )
    # Facebook redirects to checkpoint or home on valid email with wrong password

    # "login_error" in response indicates unrecognized email

    if "login_error" in resp.text or resp.status_code == 200:
        return {
            "email": email,
            "status": "not_found",
            "exists": False,
            "method": "login"
        }
    # 302 redirect or "checkpoint" indicates valid credentials exist

    return {
        "email": email,
        "status": "login_requires_password",
        "exists": True,
        "method": "login"
    }

Facebook's login flow distinguishes between unrecognized emails (stays on login page with error) and valid emails with wrong passwords (redirects to checkpoint or recovery). The module exploits this behavioral difference.


Password Recovery Module: Odnoklassniki

File: holehe/modules/social_media/odnoklassniki.py

method = "password recovery"

async def check(email: str, client):
    headers = {
        "User-Agent": "Mozilla/5.0",
        "Accept": "application/json",
        "Content-Type": "application/x-www-form-urlencoded"
    }
    data = {
        "st.email": email,
        "st.friend": "",
        "st.phone": ""
    }
    resp = await client.post(
        "https://ok.ru/dk?cmd=anonymRecoveryStart&st.cmd=anonymRecoveryStart",
        headers=headers,
        data=data
    )
    try:
        result = resp.json()
    except ValueError:
        return {"email": email, "status": "error", "exists": False}
    
    # Masked email in response confirms account exists

    masked_email = result.get("maskedLogin")
    if masked_email:
        return {
            "email": email,
            "status": "recovery_sent",
            "exists": True,
            "masked_email": masked_email,
            "method": "password recovery"
        }
    # "EMAIL_NOT_FOUND" or similar indicates no account

    if result.get("error", "").upper() == "EMAIL_NOT_FOUND":
        return {"email": email, "status": "not_found", "exists": False}
    return {"email": email, "status": "unknown", "exists": False}

Odnoklassniki's recovery endpoint returns the masked registered email when found, providing explicit confirmation without requiring authentication.


Core Engine Dispatch Logic

The central orchestration in holehe/core.py loads modules and normalizes their execution:


# Simplified excerpt from holehe/core.py

async def run_module(module, email: str, client: httpx.AsyncClient) -> dict:
    """Execute a single module and standardize its output."""
    method = getattr(module, "method", "unknown")
    result = await module.check(email, client)
    
    # Enrich with metadata

    result["module"] = module.__name__.split(".")[-1]
    result["method"] = method
    result["category"] = categorize_method(method)  # register/login/recovery

    
    return result

def categorize_method(method: str) -> str:
    """Normalize method strings for reporting."""
    method_lower = method.lower()
    if "register" in method_lower:
        return "registration"
    if "login" in method_lower:
        return "authentication"
    if "recovery" in method_lower or "reset" in method_lower:
        return "password_recovery"
    return "unknown"

This abstraction allows modules to evolve independently while providing consistent output formatting for CLI and programmatic consumers.


Why Method Selection Matters

Detection reliability varies by service. Some platforms aggressively rate-limit login attempts but permit registration checks. Others expose password recovery without authentication, making it the only viable vector.

Legal and ethical considerations differ. Password recovery flows are designed for public access and typically include CAPTCHAs or rate limiting that respects normal user behavior. Login attempts with invalid credentials may violate terms of service more explicitly in some jurisdictions.

Result granularity impacts investigations. Register methods identify account existence directly. Login methods can additionally reveal account status (active, suspended, requiring 2FA). Recovery methods sometimes expose partial email masking or associated phone numbers.


Summary

  • register modules confirm accounts by attempting signup and intercepting "already exists" errors.

  • login modules use invalid passwords to distinguish between non-existent accounts and existing ones with wrong credentials.

  • password recovery modules exploit reset flows that confirm registration through success messages or masked email returns.

  • All three implement an identical check(email, client) interface consumed by holehe/core.py.

  • Method selection depends on service behavior, rate limits, and detection reliability requirements.

  • The modular architecture enables community contributions without core engine modifications.


Frequently Asked Questions

How does Holehe determine which method to use for a specific service?

Each module hardcodes its method constant based on the contributor's analysis of which endpoint provides reliable enumeration. The core engine does not auto-select; it executes whatever the module declares. Contributors choose methods by testing service responses and selecting the most stable indicator of account existence.

Can a single service module implement multiple methods?

No—Holehe's architecture expects one method per module. Services with multiple viable endpoints are typically implemented as separate modules (e.g., twitter_register.py vs twitter_recovery.py) or the contributor selects the most reliable single approach. The method constant must be a string, not a list.

Why do some modules fail while others succeed for the same email?

Services implement different anti-automation protections. Login endpoints often have stricter rate limiting and CAPTCHA requirements than registration or recovery endpoints. Additionally, geographic IP restrictions and session fingerprinting affect some methods more than others. The method type directly influences success probability against specific targets.

Is the password recovery method always the safest option?

Generally yes for avoidance of rate limits, but not universally. Some services email account holders when recovery is triggered, potentially alerting targets to investigation. Others require additional verification (SMS, backup codes) that modules cannot complete, resulting in inconclusive results. Method safety depends on the specific service's implementation and the investigator's threat model.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →