feat(users): support creating app users with a Firebase Auth account
New users can now be created with a password, which creates a real Firebase Auth account (so they can log into the mobile app immediately) alongside the Firestore profile document. UserCreate is now UserProfile + password (request-only, never persisted or echoed back); deleting a user also removes their Auth account. - backend/users: split UserCreate into UserProfile (persisted shape) and UserCreate (adds password), wire firebase_auth create/delete - CreateUserModal: new lightweight modal for creating a user from other flows (e.g. device onboarding) without leaving the page - UserForm: adds the password field for new users; also fixes useToast() being used undestructured (toast.success(...) was being called on the hook's return value instead of its .toast method) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -84,12 +84,28 @@ def get_user(user_doc_id: str) -> UserInDB:
|
||||
|
||||
|
||||
def create_user(data: UserCreate) -> UserInDB:
|
||||
"""Create a new user document in Firestore."""
|
||||
"""Create a new user: a Firebase Auth account (so they can log into the app
|
||||
immediately) plus the matching Firestore profile document."""
|
||||
if not data.password or len(data.password) < 6:
|
||||
raise ValidationError("Password must be at least 6 characters.")
|
||||
if not data.email:
|
||||
raise ValidationError("Email is required.")
|
||||
|
||||
db = get_db()
|
||||
doc_data = data.model_dump()
|
||||
doc_data = data.model_dump(exclude={"password"})
|
||||
doc_data["friendsList"] = []
|
||||
doc_data["friendsInvited"] = []
|
||||
|
||||
try:
|
||||
firebase_user = firebase_auth.create_user(
|
||||
email=data.email,
|
||||
password=data.password,
|
||||
display_name=data.display_name or None,
|
||||
)
|
||||
except firebase_auth.EmailAlreadyExistsError:
|
||||
raise ValidationError(f"A user with email {data.email} already exists.")
|
||||
doc_data["uid"] = firebase_user.uid
|
||||
|
||||
_, doc_ref = db.collection(COLLECTION).add(doc_data)
|
||||
|
||||
return UserInDB(id=doc_ref.id, **doc_data)
|
||||
@@ -111,13 +127,20 @@ def update_user(user_doc_id: str, data: UserUpdate) -> UserInDB:
|
||||
|
||||
|
||||
def delete_user(user_doc_id: str) -> None:
|
||||
"""Delete a user document from Firestore."""
|
||||
"""Delete a user document from Firestore, plus their Firebase Auth account if one exists."""
|
||||
db = get_db()
|
||||
doc_ref = db.collection(COLLECTION).document(user_doc_id)
|
||||
doc = doc_ref.get()
|
||||
if not doc.exists:
|
||||
raise NotFoundError("User")
|
||||
|
||||
uid = doc.to_dict().get("uid", "")
|
||||
if uid:
|
||||
try:
|
||||
firebase_auth.delete_user(uid)
|
||||
except firebase_auth.UserNotFoundError:
|
||||
pass
|
||||
|
||||
doc_ref.delete()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user