From 284700dd6057a25bc05f9bca4e573bd375561d78 Mon Sep 17 00:00:00 2001 From: LIghtJUNction Date: Wed, 29 Apr 2026 20:28:22 +0800 Subject: [PATCH] feat(dashboard): support first-login auto admin setup When the dashboard password is still the default (not configured), the user's first login attempt automatically saves their chosen username and password as the admin credentials, eliminating the need to pre-set a password via CLI. --- astrbot/dashboard/routes/auth.py | 49 ++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/astrbot/dashboard/routes/auth.py b/astrbot/dashboard/routes/auth.py index f40513a1c..c94620b79 100644 --- a/astrbot/dashboard/routes/auth.py +++ b/astrbot/dashboard/routes/auth.py @@ -65,8 +65,8 @@ class AuthRoute(Route): ) async def login(self): - username = self.config["dashboard"]["username"] - password = self.config["dashboard"]["password"] + stored_username = self.config["dashboard"]["username"] + stored_password = self.config["dashboard"]["password"] post_data = await request.json req_username = ( @@ -81,22 +81,45 @@ class AuthRoute(Route): req_password_proof = ( post_data.get("password_proof") if isinstance(post_data, dict) else None ) - if not isinstance(req_username, str): + if not isinstance(req_username, str) or not isinstance(req_password, str): return Response().error("Invalid request payload").__dict__ + # First login: if password is not configured (empty/default), trust the user's input + if is_default_dashboard_password(stored_password) and not DEMO_MODE: + # First-time setup: save user's input as admin credentials + new_password_hash = hash_dashboard_password(req_password) + self.config["dashboard"]["username"] = req_username + self.config["dashboard"]["password"] = new_password_hash + self.config.save_config() + logger.warning( + f"Dashboard admin configured via first login: username={req_username}", + ) + return ( + Response() + .ok( + { + "token": self.generate_jwt(req_username), + "username": req_username, + "change_pwd_hint": False, + "legacy_pwd_hint": False, + }, + ) + .__dict__ + ) + login_verified = False if isinstance(req_password, str): - login_verified = req_username == username and verify_dashboard_password( - password, + login_verified = req_username == stored_username and verify_dashboard_password( + stored_password, req_password, ) elif isinstance(req_challenge_id, str) and isinstance(req_password_proof, str): challenge_nonce = self._consume_login_challenge(req_challenge_id) login_verified = ( - req_username == username + req_username == stored_username and isinstance(challenge_nonce, str) and verify_dashboard_login_proof( - password, + stored_password, challenge_nonce, req_password_proof, ) @@ -106,12 +129,8 @@ class AuthRoute(Route): if login_verified: change_pwd_hint = False - legacy_pwd_hint = is_legacy_dashboard_password(password) - if ( - username == "astrbot" - and is_default_dashboard_password(password) - and not DEMO_MODE - ): + legacy_pwd_hint = is_legacy_dashboard_password(stored_password) + if is_default_dashboard_password(stored_password) and not DEMO_MODE: change_pwd_hint = True logger.warning( "The dashboard is using the default password, please change it immediately to ensure security.", @@ -122,8 +141,8 @@ class AuthRoute(Route): Response() .ok( { - "token": self.generate_jwt(username), - "username": username, + "token": self.generate_jwt(stored_username), + "username": stored_username, "change_pwd_hint": change_pwd_hint, "legacy_pwd_hint": legacy_pwd_hint, },