Initial FastAPI admin auth scaffold
This commit is contained in:
1
app/common/security/__init__.py
Normal file
1
app/common/security/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Security helpers."""
|
||||
40
app/common/security/password_hasher.py
Normal file
40
app/common/security/password_hasher.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import base64
|
||||
import hashlib
|
||||
import hmac
|
||||
import secrets
|
||||
|
||||
ALGORITHM = "pbkdf2_sha256"
|
||||
ITERATIONS = 390_000
|
||||
|
||||
|
||||
def _b64encode(raw: bytes) -> str:
|
||||
return base64.urlsafe_b64encode(raw).decode("ascii").rstrip("=")
|
||||
|
||||
|
||||
def _b64decode(value: str) -> bytes:
|
||||
padding = "=" * (-len(value) % 4)
|
||||
return base64.urlsafe_b64decode(value + padding)
|
||||
|
||||
|
||||
def hash_password(password: str, iterations: int = ITERATIONS) -> str:
|
||||
salt = secrets.token_bytes(16)
|
||||
digest = hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"), salt, iterations)
|
||||
return f"{ALGORITHM}${iterations}${_b64encode(salt)}${_b64encode(digest)}"
|
||||
|
||||
|
||||
def verify_password(password: str, stored_hash: str) -> bool:
|
||||
try:
|
||||
algorithm, iterations, salt, expected = stored_hash.split("$", 3)
|
||||
except ValueError:
|
||||
return hmac.compare_digest(password, stored_hash)
|
||||
|
||||
if algorithm != ALGORITHM:
|
||||
return False
|
||||
|
||||
digest = hashlib.pbkdf2_hmac(
|
||||
"sha256",
|
||||
password.encode("utf-8"),
|
||||
_b64decode(salt),
|
||||
int(iterations),
|
||||
)
|
||||
return hmac.compare_digest(_b64encode(digest), expected)
|
||||
Reference in New Issue
Block a user