Passwords

4 endpoints for the full password lifecycle — from hashing at registration to strength feedback at input time.

Method Endpoint Purpose
POST /v1/auth/hash-password Hash a password with bcrypt, argon2, or scrypt
POST /v1/auth/verify-password Verify a password against a stored hash
POST /v1/auth/password-strength Analyse password strength with crack-time estimate
GET /v1/auth/generate-password Generate cryptographically secure random passwords

Python SDK Examples

Hash a password at registration

hash_password supports three modern algorithms. Argon2id is the recommended default — it was the winner of the Password Hashing Competition and is resistant to both GPU and side-channel attacks.

from toolkitapi import Auth

auth = Auth(api_key="tk_...")

# Argon2id — recommended default
result = auth.hash_password("hunter2", algorithm="argon2")
print(result["hash"])
# $argon2id$v=19$m=65536,t=3,p=4$...

# bcrypt — widely supported, cost factor 12 is the modern baseline
bcrypt_result = auth.hash_password("hunter2", algorithm="bcrypt", rounds=12)
print(bcrypt_result["hash"])
# $2b$12$...

# scrypt — log2(N) work factor
scrypt_result = auth.hash_password("hunter2", algorithm="scrypt", rounds=14)
print(scrypt_result["hash"])
# $scrypt$ln=14,r=8,p=1$...

Full registration flow

from toolkitapi import Auth

auth = Auth(api_key="tk_...")

def register_user(username: str, plaintext_password: str) -> dict:
    """Hash the password and return the user record to store."""
    result = auth.hash_password(plaintext_password, algorithm="argon2")
    return {
        "username": username,
        "password_hash": result["hash"],
        "hash_algorithm": result["algorithm"],
    }

user = register_user("alice", "correct horse battery staple")
# Store user["password_hash"] in your database — never the plaintext

Verify a password at login

verify_password automatically detects the algorithm from the hash prefix — no need to store or pass the algorithm separately.

from toolkitapi import Auth

auth = Auth(api_key="tk_...")

stored_hash = "$argon2id$v=19$m=65536,t=3,p=4$..."

result = auth.verify_password("hunter2", stored_hash)
print(result["valid"])          # True or False
print(result["needs_rehash"])   # True if parameters are below current recommendations

Full login flow with rehash detection

When needs_rehash is True the stored hash used a lower cost factor than the current recommendation. Upgrade it transparently during a successful login (when you still have the plaintext).

from toolkitapi import Auth

auth = Auth(api_key="tk_...")

def verify_login(
    plaintext: str,
    stored_hash: str,
    update_hash_callback,
) -> bool:
    """Verify login and silently upgrade the hash if needed."""
    result = auth.verify_password(plaintext, stored_hash)

    if not result["valid"]:
        return False

    if result["needs_rehash"]:
        # Re-hash with current parameters and persist the new hash
        new_result = auth.hash_password(plaintext, algorithm="argon2")
        update_hash_callback(new_result["hash"])

    return True

Analyse password strength

password_strength uses the zxcvbn library — it models real attacker patterns (dictionary words, common substitutions, keyboard patterns, dates) rather than simple character-class rules.

from toolkitapi import Auth

auth = Auth(api_key="tk_...")

passwords = [
    "password",
    "p@ssw0rd",
    "correct horse battery staple",
    "Tr0ub4dor&3",
    "Xk#9mQ!2vLpR",
]

for pw in passwords:
    result = auth.password_strength(pw)
    score = result["score"]          # 0 (very weak) to 4 (very strong)
    crack  = result["crack_time_display"]
    print(f"{pw:30} score={score}  crack={crack}")
    for suggestion in result["feedback"]["suggestions"]:
        print(f"  → {suggestion}")

Output:

password                       score=0  crack=less than a second
  → Add another word or two. Uncommon words are better.
  → Use a few words, avoid common phrases
p@ssw0rd                       score=0  crack=less than a second
  → Predictable substitutions like '@' instead of 'a' don't help very much
correct horse battery staple   score=3  crack=centuries
Tr0ub4dor&3                    score=2  crack=months
Xk#9mQ!2vLpR                   score=4  crack=centuries

Real-time strength indicator

from toolkitapi import Auth

auth = Auth(api_key="tk_...")

STRENGTH_LABELS = {0: "Very Weak", 1: "Weak", 2: "Fair", 3: "Strong", 4: "Very Strong"}
MIN_SCORE = 2  # Require "Fair" or better

def check_password_policy(password: str) -> tuple[bool, str, list[str]]:
    """Return (passes_policy, strength_label, suggestions)."""
    result = auth.password_strength(password)
    score = result["score"]
    label = STRENGTH_LABELS[score]
    suggestions = result["feedback"]["suggestions"]
    warnings = result["feedback"]["warnings"]
    if warnings:
        suggestions = warnings + suggestions
    return score >= MIN_SCORE, label, suggestions

Generate secure random passwords

generate_password uses Python's secrets module for cryptographically secure randomness. All character set and length options are applied server-side before the entropy calculation.

from toolkitapi import Auth

auth = Auth(api_key="tk_...")

# Single password — defaults: 20 chars, all character sets
result = auth.generate_password()
print(result["passwords"][0])   # e.g. "Kx#9mQ!2vLpR$Tn7wBqZ"
print(result["entropy_bits"])   # e.g. 131.1

# Batch of 10 — system-generated temporary passwords
batch = auth.generate_password(count=10, length=16, symbols=False)
for pw in batch["passwords"]:
    print(pw)

# Human-readable — no ambiguous characters (0/O/1/l/I)
readable = auth.generate_password(
    length=24,
    exclude_ambiguous=True,
)
print(readable["passwords"][0])

Generate passwords for a bulk user import

from toolkitapi import Auth

auth = Auth(api_key="tk_...")

def provision_temp_passwords(user_count: int) -> list[str]:
    """Generate one temporary password per user, up to 50 at a time."""
    passwords = []
    for batch_start in range(0, user_count, 50):
        batch_size = min(50, user_count - batch_start)
        result = auth.generate_password(
            count=batch_size,
            length=20,
            exclude_ambiguous=True,
        )
        passwords.extend(result["passwords"])
    return passwords

Response Fields

hash-password response:

Field Type Description
hash string Hashed password string
algorithm string Algorithm used: bcrypt, argon2, or scrypt
parameters object Algorithm-specific cost parameters

verify-password response:

Field Type Description
valid bool Whether the password matches the hash
algorithm_detected string Algorithm detected from the hash format
needs_rehash bool true if parameters are below current recommendations

password-strength response:

Field Type Description
score int 0 (very weak) – 4 (very strong)
crack_time_display string Human-readable offline crack time estimate
entropy_bits float Estimated entropy in bits
feedback.warnings list Specific warnings about the password
feedback.suggestions list Suggestions to improve strength

generate-password response:

Field Type Description
passwords list Generated password strings
entropy_bits float Bits of entropy per password

Tip

For new user registrations, run password_strength before hash_password so you can reject weak passwords before hashing. The score field maps directly to a UI strength bar: 0–1 = reject, 2 = warn, 3–4 = accept. Only hash passwords that pass your minimum strength threshold.