50 lines
1.1 KiB
Python
50 lines
1.1 KiB
Python
import hashlib
|
|
import hmac
|
|
import secrets
|
|
from dataclasses import dataclass
|
|
|
|
|
|
PBKDF2_ITERATIONS = 120_000
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class AuthenticatedUser:
|
|
id: str
|
|
username: str
|
|
nickname: str
|
|
|
|
|
|
def hash_password(password: str) -> str:
|
|
salt = secrets.token_hex(16)
|
|
digest = hashlib.pbkdf2_hmac(
|
|
"sha256",
|
|
password.encode("utf-8"),
|
|
salt.encode("ascii"),
|
|
PBKDF2_ITERATIONS,
|
|
).hex()
|
|
return f"pbkdf2_sha256${PBKDF2_ITERATIONS}${salt}${digest}"
|
|
|
|
|
|
def verify_password(password: str, stored: str) -> bool:
|
|
try:
|
|
algorithm, iterations, salt, digest = stored.split("$", 3)
|
|
if algorithm != "pbkdf2_sha256":
|
|
return False
|
|
calculated = hashlib.pbkdf2_hmac(
|
|
"sha256",
|
|
password.encode("utf-8"),
|
|
salt.encode("ascii"),
|
|
int(iterations),
|
|
).hex()
|
|
return hmac.compare_digest(calculated, digest)
|
|
except (TypeError, ValueError):
|
|
return False
|
|
|
|
|
|
def new_token() -> str:
|
|
return secrets.token_urlsafe(48)
|
|
|
|
|
|
def hash_token(token: str) -> str:
|
|
return hashlib.sha256(token.encode("utf-8")).hexdigest()
|