feat(auth): make email the unique login identifier
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled

Login is now by email, not username. username still exists internally
(JWT sub claim, audit logs, Jira actor attribution, SSO provisioning all
still key off it) but is now always kept equal to email everywhere a user
is created or their email changes — never a separately-chosen value.

- User.email is now unique + NOT NULL (migration b067 backfills any
  missing/blank email from username first, so existing rows — notably
  the seeded admin, which historically had none — never violate it).
- /auth/login and the (unused but updated for consistency)
  authenticate_user() now query by email.
- create_user (legacy, unreferenced but kept) and
  create_user_without_password both derive username from email.
- update_user keeps username in sync when email changes, and rejects
  duplicate emails.
- seed.py reads ADMIN_EMAIL (new env var, wired through install.sh and
  docker-compose.prod.yml) for the initial admin; falls back to an
  email-shaped ADMIN_USERNAME or a placeholder that's flagged for the
  operator to fix.
- admin_config.py's import bundle now matches/creates users by email,
  skipping (not crashing on) entries with no email.
- sso_service.py always sets username = email for SSO-provisioned users.
- LoginPage/auth.ts updated to email input/copy (wire field name stays
  'username' — that's the OAuth2PasswordRequestForm spec, not the value).
This commit is contained in:
kitos
2026-07-23 14:39:36 +02:00
parent 0a6cb5510c
commit 504dfc52f5
21 changed files with 184 additions and 81 deletions
+18 -4
View File
@@ -195,6 +195,17 @@ if [ "$SKIP_CONFIG" = false ]; then
read -r INPUT_ADMIN_USER
ADMIN_USERNAME="${INPUT_ADMIN_USER:-admin}"
echo ""
echo -e " ${DIM}Email is what you'll actually log in with (the unique identifier).${NC}"
print_prompt "Admin email: "
read -r INPUT_ADMIN_EMAIL
while [ -z "$INPUT_ADMIN_EMAIL" ]; do
print_warn "An admin email is required."
print_prompt "Admin email: "
read -r INPUT_ADMIN_EMAIL
done
ADMIN_EMAIL="$INPUT_ADMIN_EMAIL"
echo ""
echo -e " ${DIM}Leave empty to auto-generate a secure password."
echo -e " The password will be shown in the installation summary.${NC}"
@@ -270,6 +281,7 @@ if [ "$SKIP_CONFIG" = false ]; then
echo -e "${BOLD} ├──────────────────────────────────────────────────────┤${NC}"
echo -e " │ URL: ${CYAN}${ORIGIN_URL}${NC}"
echo -e " │ Admin user: ${CYAN}${ADMIN_USERNAME}${NC}"
echo -e " │ Admin email: ${CYAN}${ADMIN_EMAIL}${NC}"
if [ "$ADMIN_PW_GENERATED" = true ]; then
echo -e " │ Admin pass: ${CYAN}(auto-generated)${NC}"
else
@@ -315,6 +327,7 @@ TOKEN_EXPIRE_MINUTES=${TOKEN_EXPIRE_MINUTES}
# ── Initial Admin Account ────────────────────────────────────────────────────
ADMIN_USERNAME=${ADMIN_USERNAME}
ADMIN_EMAIL=${ADMIN_EMAIL}
ADMIN_PASSWORD=${ADMIN_PASSWORD}
# ── MinIO Object Storage ─────────────────────────────────────────────────────
@@ -422,6 +435,8 @@ echo -e " ${GREEN}ready${NC}"
print_ok "All services are running"
# ── Extract admin credentials from backend logs ──────────────────────
# Login is by email (the unique identifier) — ADMIN_CREDS_USER holds that
# email despite the variable name, kept for minimal diff below.
ADMIN_CREDS_USER=""
ADMIN_CREDS_PASS=""
@@ -430,14 +445,13 @@ ADMIN_CREDS_PASS=""
LOG_OUTPUT=$(docker logs aegis-backend 2>&1 | tail -20)
if echo "$LOG_OUTPUT" | grep -q "Initial Admin User Created"; then
ADMIN_CREDS_USER=$(echo "$LOG_OUTPUT" | grep "Username :" | sed 's/.*Username : //')
ADMIN_CREDS_USER=$(echo "$LOG_OUTPUT" | grep "Login (email) :" | sed 's/.*Login (email) : //')
ADMIN_CREDS_PASS=$(echo "$LOG_OUTPUT" | grep "Password :" | sed 's/.*Password : //')
fi
# Fallback: if we set it via env, use those values
if [ -z "$ADMIN_CREDS_USER" ]; then
ADMIN_CREDS_USER=$(grep ADMIN_USERNAME "$ENV_FILE" 2>/dev/null | cut -d= -f2 || echo "admin")
ADMIN_CREDS_USER="${ADMIN_CREDS_USER:-admin}"
ADMIN_CREDS_USER=$(grep '^ADMIN_EMAIL=' "$ENV_FILE" 2>/dev/null | cut -d= -f2-)
fi
if [ -z "$ADMIN_CREDS_PASS" ] || [ "$ADMIN_CREDS_PASS" = "(set via ADMIN_PASSWORD env var)" ]; then
@@ -531,7 +545,7 @@ echo -e "${BOLD}║${NC}
echo -e "${BOLD}╠══════════════════════════════════════════════════════════════╣${NC}"
echo -e "${BOLD}${NC} ${BOLD}${NC}"
echo -e "${BOLD}${NC} ${CYAN}Admin Login${NC} ${BOLD}${NC}"
echo -e "${BOLD}${NC} Username: ${GREEN}${ADMIN_CREDS_USER}${NC}"
echo -e "${BOLD}${NC} Email: ${GREEN}${ADMIN_CREDS_USER}${NC}"
if [ -n "$ADMIN_CREDS_PASS" ]; then
echo -e "${BOLD}${NC} Password: ${GREEN}${ADMIN_CREDS_PASS}${NC}"
else