๐Ÿ“ Python

Password Hashing with Argon2 and pwdlib

P
Author
PyLand Team
๐Ÿ“…
Published
30.06.2026
โฑ๏ธ
Reading time
1 min
๐Ÿ‘๏ธ
Views
2185
๐ŸŒณ
Level
Advanced

If a database is leaked, strong password hashing prevents immediate recovery of plaintext passwords and makes offline guessing expensive. It does not make weak passwords impossible to crack, so password policy, rate limiting, and multi-factor authentication still matter.

Why you must not store passwords in plain text

Store only salted password hashes, never plaintext passwords. For new applications, use a maintained password-hashing library and its recommended algorithm and parameters.

pwdlib with Argon2

uv add "pwdlib[argon2]"
from pwdlib import PasswordHash

password_hash = PasswordHash.recommended()

# Hash a password
hashed = password_hash.hash("my_password")
# '$argon2id$...' โ€” Argon2id hash

# Verify a password
is_valid = password_hash.verify("my_password", hashed)    # True
is_valid = password_hash.verify("wrong_password", hashed) # False

Why Argon2id

  • Designed for password hashing and resistant to GPU-based guessing
  • Uses a unique salt, so identical passwords produce different hashes
  • Has configurable time, memory, and parallelism costs
  • PasswordHash.recommended() selects maintained recommended settings

In FastAPI / SQLModel

from pwdlib import PasswordHash

password_hash = PasswordHash.recommended()

class User(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    username: str = Field(unique=True)
    hashed_password: str

def create_user(session: Session, username: str, password: str) -> User:
    user = User(
        username=username,
        hashed_password=password_hash.hash(password),
    )
    session.add(user)
    session.commit()
    return user

def authenticate(session: Session, username: str, password: str) -> User | None:
    user = session.exec(select(User).where(User.username == username)).first()
    if not user or not password_hash.verify(password, user.hashed_password):
        return None
    return user

Existing bcrypt hashes

uv add "pwdlib[argon2,bcrypt]"

Argon2id is the preferred choice for new hashes. If an existing system already contains bcrypt hashes, configure a migration path that verifies the legacy hash and replaces it with Argon2id after the user’s next successful login. Do not silently change algorithms without testing compatibility with stored hashes.

Your reaction to the article

๐Ÿ’ฌ Comments (0)

๐Ÿ” Sign in to leave a comment
๐Ÿšช Login
๐Ÿ’ญ

No comments yet

Be the first to share your opinion about this article!

๐Ÿ”— Similar

Similar articles

Continue learning with these materials

๐Ÿ“

if __name__ == "__main__": the Python entry point

The if name == "main" guard defines a Python program's entry point: it distinguishes direct...

๐Ÿ“… 14.08.2026 ๐Ÿ‘๏ธ 43
๐Ÿ“

strip() and lower(): Preparing User Text ๐Ÿงน

A user may enter the correct word with extra spaces or different letter case. Python...

๐Ÿ“… 11.08.2026 ๐Ÿ‘๏ธ 49
๐Ÿ“

ord(), chr(), and Cyclic Letter Shifts in Python ๐Ÿ”

A string contains characters, but a computer stores every character as a numeric code. Python...

๐Ÿ“… 09.08.2026 ๐Ÿ‘๏ธ 88
๐ŸŽ“ Continue learning

Courses that cover this material

Visit the course to apply this material in practice.

FastAPI: From First Route to an AI-Powered Site Open course curriculum