first commit
This commit is contained in:
commit
d416fe4cce
69 changed files with 8269 additions and 0 deletions
68
backend/app/services/auth.py
Normal file
68
backend/app/services/auth.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
from datetime import datetime, timezone
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
from motor.motor_asyncio import AsyncIOMotorDatabase
|
||||
from pymongo.errors import DuplicateKeyError
|
||||
|
||||
from app.core.security import hash_auth_key, normalize_identifier, verify_auth_key
|
||||
from app.schemas.auth import AuthLoginRequest, AuthSignupRequest
|
||||
|
||||
ACCOUNTS_COLLECTION = "accounts"
|
||||
|
||||
|
||||
async def ensure_auth_indexes(database: AsyncIOMotorDatabase) -> None:
|
||||
await database[ACCOUNTS_COLLECTION].create_index(
|
||||
[("identifierType", 1), ("identifier", 1)],
|
||||
unique=True,
|
||||
name="uq_accounts_identifier",
|
||||
)
|
||||
|
||||
|
||||
def _account_filter(identifier_type: str, identifier: str) -> dict[str, str]:
|
||||
return {
|
||||
"identifierType": identifier_type,
|
||||
"identifier": normalize_identifier(identifier_type, identifier),
|
||||
}
|
||||
|
||||
|
||||
async def create_account(database: AsyncIOMotorDatabase, payload: AuthSignupRequest) -> str:
|
||||
now = datetime.now(timezone.utc)
|
||||
account_document = {
|
||||
"identifierType": payload.identifierType.value,
|
||||
"identifier": normalize_identifier(payload.identifierType.value, payload.identifier),
|
||||
"hashedAuthKey": hash_auth_key(payload.authKeyMaterial),
|
||||
"saltAuth": payload.saltAuth,
|
||||
"saltEnc": payload.saltEnc,
|
||||
"createdAt": now,
|
||||
"updatedAt": now,
|
||||
}
|
||||
|
||||
try:
|
||||
result = await database[ACCOUNTS_COLLECTION].insert_one(account_document)
|
||||
except DuplicateKeyError as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail="An account with that identifier already exists.",
|
||||
) from exc
|
||||
|
||||
return str(result.inserted_id)
|
||||
|
||||
|
||||
async def get_account_by_identifier(database: AsyncIOMotorDatabase, identifier_type: str, identifier: str) -> dict[str, str]:
|
||||
account = await database[ACCOUNTS_COLLECTION].find_one(_account_filter(identifier_type, identifier))
|
||||
if account is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Account not found.",
|
||||
)
|
||||
return account
|
||||
|
||||
|
||||
async def verify_account_login(database: AsyncIOMotorDatabase, payload: AuthLoginRequest) -> str:
|
||||
account = await get_account_by_identifier(database, payload.identifierType.value, payload.identifier)
|
||||
if not verify_auth_key(payload.authKeyMaterial, account["hashedAuthKey"]):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid credentials.",
|
||||
)
|
||||
return str(account["_id"])
|
||||
Loading…
Add table
Add a link
Reference in a new issue