feat: Google OAuth sign-in + per-user Gemini API key
Users with Google accounts can now sign in without a password. Auth flow: - GET /auth/google → Google consent page (CSRF state cookie) - GET /auth/google/callback → exchange code, lookup user, set JWT - auth.json gains google_sub + google_email fields - set_password() no longer overwrites unrelated auth.json fields Admin setup: python manage_passwords.py google-add <username> <email> # add GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET to .env Per-user Gemini key: - get_user_gemini_key() reads gemini_api_key from auth.json - orchestrator_engine.run() accepts gemini_api_key param - orchestrator router passes user's key, falls back to server key login.html: "Sign in with Google" button above the password form. manage_passwords.py list: now shows auth method columns (pw / google). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,9 +6,10 @@ Usage:
|
||||
python manage_passwords.py set <username> # prompt for password
|
||||
python manage_passwords.py set <username> <pass> # set directly (avoid in shell history)
|
||||
python manage_passwords.py check <username> # test a password interactively
|
||||
python manage_passwords.py list # show users, passwords, and emails
|
||||
python manage_passwords.py list # show users, auth methods, and emails
|
||||
python manage_passwords.py invite <username> [email] # generate + optionally email invite link
|
||||
python manage_passwords.py email <username> <email> # store/update an email address
|
||||
python manage_passwords.py google-add <username> <email> # register a user for Google sign-in
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -18,7 +19,7 @@ import getpass
|
||||
# Add cortex/ to path so we can import config and auth_utils
|
||||
sys.path.insert(0, str(__import__('pathlib').Path(__file__).parent))
|
||||
|
||||
from auth_utils import set_password, check_credentials, _auth_path, create_invite
|
||||
from auth_utils import set_password, check_credentials, _auth_path, create_invite, link_google, _read_auth
|
||||
from persona import list_users
|
||||
from config import settings
|
||||
|
||||
@@ -96,10 +97,14 @@ def cmd_list(_args):
|
||||
if not users:
|
||||
print(" No users found in home/")
|
||||
return
|
||||
print(f" {'USER':<18} {'PW':<6} {'GOOGLE':<8} {'EMAIL'}")
|
||||
print(f" {'-'*18} {'-'*6} {'-'*8} {'-'*30}")
|
||||
for user in users:
|
||||
has_pw = "✓ pw" if _auth_path(user).exists() else "✗ pw"
|
||||
email = get_email(user) or "—"
|
||||
print(f" {user:<20} {has_pw} {email}")
|
||||
auth = _read_auth(user)
|
||||
has_pw = "✓" if auth.get("password_hash") else "—"
|
||||
google = auth.get("google_email") or "—"
|
||||
email = get_email(user) or "—"
|
||||
print(f" {user:<18} {has_pw:<6} {google:<36} {email}")
|
||||
|
||||
|
||||
def cmd_email(args):
|
||||
@@ -149,6 +154,21 @@ def cmd_invite(args):
|
||||
print("Tip: python manage_passwords.py invite <username> <email> to email it next time.\n")
|
||||
|
||||
|
||||
def cmd_google_add(args):
|
||||
if len(args) < 2:
|
||||
print("Usage: manage_passwords.py google-add <username> <google_email>")
|
||||
sys.exit(1)
|
||||
username, email = args[0], args[1].lower().strip()
|
||||
|
||||
# Ensure the user directory exists
|
||||
(settings.home_root() / username).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Store email; google_sub will be filled in on first sign-in
|
||||
link_google(username, sub="", email=email)
|
||||
print(f"Google sign-in registered for {username!r}: {email}")
|
||||
print(f"They can now sign in at {settings.cortex_base_url}/login using that Google account.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__)
|
||||
@@ -167,6 +187,8 @@ if __name__ == "__main__":
|
||||
cmd_email(rest)
|
||||
elif command == "invite":
|
||||
cmd_invite(rest)
|
||||
elif command == "google-add":
|
||||
cmd_google_add(rest)
|
||||
else:
|
||||
print(f"Unknown command: {command}")
|
||||
print(__doc__)
|
||||
|
||||
Reference in New Issue
Block a user