Skip to content

[SIP-201] Comprehensive Password & Credential Management Overhaul #37927

@EnxDev

Description

@EnxDev

Motivation

Apache Superset currently lacks essential, modern password management capabilities when AUTH_TYPE = AUTH_DB is configured. The existing user experience is fragmented, insecure in certain areas, and heavily reliant on server-side CLI access for basic account recovery operations. This creates a significant operational burden for administrators and a frustrating experience for end users.

Current state of affairs:

  1. Authenticated password change — A logged-in user can change their own password via Profile → Change Password, but the form does not require the current password, making it vulnerable to session hijacking attacks. This was reported in [Issue #37700](Reset My Password and Reset Password views are still accessible in Superset 6.0.0 after the major frontend porting. #37700) and partially addressed by [PR #37773](fix(auth): gate legacy FAB password reset routes behind config flag #37773) which gates legacy FAB routes behind a config flag, but the underlying UX and security gaps remain.

  2. Forgotten password (self-service reset) — There is no "Forgot Password" flow out of the box. When a user loses access to their account, the only remediation paths are:

    • An administrator runs superset fab reset-password --username <user> --password <new_password> on the server (requires shell access).
    • The administrator deactivates the user and recreates the account, losing ownership references and audit history.
    • Direct SQL manipulation of the ab_user table (highly discouraged).

    This gap has been a recurring community request since at least 2017 ([Issue #2731](Give users the rights to change their password #2731)), with dedicated discussions ([#18427](Forgot Password feature #18427), [#34241](🔐 Forgot Password Feature – Superset #34241)) and feature requests ([#19498](Need a way to change a forgotten password #19498), [#36223](Admin UI: allow administrators to change/reset another user's password #36223)).

  3. Admin-initiated password reset via UI — Administrators cannot reset another user's password from the web interface. [PR #36764](feat(dashboard): add password change functionality in user management #36764) adds password change functionality to the user management modal, but a holistic approach covering security policies, audit logging, and session invalidation is still missing.

  4. Email change — There is no CLI or UI mechanism to update a user's email address. If a user loses access to their email, the administrator has no supported path to update it without direct database edits.

  5. Critical operational scenarios — In on-premise and self-hosted deployments, three scenarios cause the most pain:

    • Total admin lockout — If the sole admin forgets their password and has no shell access, recovery is extremely difficult.
    • Missing automated deprovisioning — No built-in mechanism to disable or expire stale accounts.
    • Ungoverned server access — CLI password resets require direct server access, which may not be available to the logical "Superset admin" in many organizations.

This SIP proposes a unified, security-conscious overhaul of password and credential management in Superset, scoped exclusively to AUTH_TYPE = AUTH_DB deployments.

Proposed Change

The proposal is organized into four pillars, each addressing a distinct area of the password lifecycle.


Pillar 1 — Secure Authenticated Password Change

When a logged-in user changes their own password:

  • Require current password — The change password form must prompt for the existing password before accepting a new one. This prevents unauthorized password changes in case of session hijacking.
  • Password policy enforcement — Introduce configurable validation rules via superset_config.py:
    • PASSWORD_MIN_LENGTH (default: 12)
    • PASSWORD_REQUIRE_UPPERCASE (default: True)
    • PASSWORD_REQUIRE_LOWERCASE (default: True)
    • PASSWORD_REQUIRE_DIGIT (default: True)
    • PASSWORD_REQUIRE_SPECIAL (default: True)
    • PASSWORD_COMMON_LIST_CHECK (default: True) — reject passwords from a known common-passwords list
  • Session invalidation — After a successful password change, all existing sessions for the user must be invalidated. The session cookie is regenerated, and any persistent tokens are revoked. This mitigates session hijacking.
  • Hashing — Passwords are hashed using bcrypt (or argon2 as a configurable alternative). Passwords must never be stored in plaintext or logged.
  • Audit logging — Every password change event is recorded with timestamp, user ID, source IP, and whether it was self-initiated or admin-initiated.

Frontend changes: Migrate the password change form from the legacy FAB SSR view to a React SPA component consistent with the post-6.0.0 architecture. The new form includes fields for current password, new password, and password confirmation with real-time client-side validation feedback.


Pillar 2 — Self-Service Forgotten Password Reset (Email-Based)

A new "Forgot Password" flow accessible from the login page:

User clicks "Forgot Password" on login page
        ↓
User enters their email address
        ↓
System generates a cryptographically secure, single-use token
        ↓
Email sent via configured SMTP with an HTTPS reset link
        ↓
Token has a configurable expiry (default: 30 minutes, range: 15–60 min)
        ↓
User clicks link → lands on password reset page
        ↓
User enters new password (subject to password policy)
        ↓
Token is invalidated immediately after use
        ↓
All existing sessions invalidated
        ↓
User is redirected to login page

Security requirements:

  • Tokens must be cryptographically random (at least 256 bits of entropy, e.g. secrets.token_urlsafe(48)).
  • Tokens are single-use and invalidated on consumption or expiry.
  • The system must not reveal whether an email address exists in the database. Regardless of whether the email is found, the user sees the same confirmation message: "If an account with that email exists, a reset link has been sent."
  • Rate limiting on the reset endpoint: configurable via PASSWORD_RESET_RATE_LIMIT (default: 5 requests per 15 minutes per IP).
  • The reset link must use HTTPS in production environments. The URL scheme is derived from the incoming request (honoring X-Forwarded-Proto) or from an explicit configuration override (e.g., PASSWORD_RESET_BASE_URL). In local development over plaintext HTTP, the link will use http:// accordingly. A startup warning is logged when PASSWORD_RESET_ENABLED = True and the detected scheme is not HTTPS.
  • Token storage in the database uses hashed values (never store the raw token).

Configuration:

# superset_config.py
PASSWORD_RESET_ENABLED = True  # default: False (opt-in)
PASSWORD_RESET_TOKEN_EXPIRY_MINUTES = 30
PASSWORD_RESET_RATE_LIMIT = "5 per 15 minutes"

Prerequisite: A working SMTP configuration (EMAIL_NOTIFICATIONS = True with valid SMTP_* settings). When SMTP is not configured and PASSWORD_RESET_ENABLED = True, Superset should log a warning at startup.


Pillar 3 — Admin-Initiated Password Reset and Email Change (UI + CLI)

Web UI — Admin password reset:

In Security → List Users, administrators (users with the Admin role) can trigger a password reset for any user. Two modes are supported:

  1. Set password directly — Admin enters a new password for the user via a modal dialog. The user's existing sessions are invalidated. Whether this mode should be supported is open for discussion: while it offers a quick recovery path (especially when SMTP is unavailable), it means the admin has momentary knowledge of the user's password, which conflicts with zero-knowledge security principles. Community input on whether to include, exclude, or gate this behind a separate config flag (e.g., ADMIN_DIRECT_PASSWORD_SET = False) is welcome.
  2. Send reset link — Admin triggers a reset email to the user's registered email (requires SMTP). This is the preferred approach as the admin never sees the new password.

Both actions are gated behind the existing can_write permission on the User resource and are recorded in the audit log.

CLI — Password reset (existing):

The existing superset fab reset-password command is preserved and documented:

superset fab reset-password --username <username> --password <new_password>

CLI — Email change (new):

A new CLI command to update a user's email address:

superset change-email --username <username> --new-email <new_email>

This command:

  • Validates email format.
  • Checks for uniqueness against existing users.
  • Updates the ab_user.email field.
  • Logs the change in the audit log.
  • Is only available when AUTH_TYPE = AUTH_DB.

This addresses the scenario where a user loses access to their email and cannot use the self-service reset flow.

CLI — Account deactivation (new):

superset deactivate-user --username <username>

Deactivates the user (active = False), invalidates all sessions, and logs the event.


Pillar 4 — Security Hardening and Operational Safety

Login rate limiting and account lockout:

  • LOGIN_RATE_LIMIT — Configurable rate limit on the login endpoint (default: 10 attempts per 5 minutes per IP).
  • LOGIN_MAX_FAILURES — After N consecutive failed login attempts for a given username, the account is temporarily locked (default: 5 failures → 15 minute lockout).
  • Lockout events are logged and optionally trigger an email notification to the user.

Audit logging:

All authentication-related events are logged to a new auth_audit_log table:

  • Successful and failed logins (with IP and user-agent)
  • Password changes (self and admin-initiated)
  • Password resets (request and completion)
  • Email changes
  • Account lockouts and unlocks
  • Account activations and deactivations

Session management hardening:

After any password or credential change:

  • Invalidate all existing session records for the user.
  • Regenerate the session ID for the current session.
  • Revoke any persistent or remember-me tokens.

Future Considerations (Out of Scope for This SIP)

The following topics are acknowledged as important but are intentionally excluded from this SIP to keep scope manageable. They may be addressed in subsequent SIPs:

  • Multi-Factor Authentication (MFA) — Strongly recommended for production deployments. Could be introduced via TOTP (e.g. pyotp) as a follow-up.
  • Password expiration policies — Configurable password rotation (e.g. every 90 days). Depends on organizational policy and is controversial in modern security guidance (NIST SP 800-63B discourages mandatory rotation).
  • OIDC / SAML integration improvements — When AUTH_TYPE is set to AUTH_OID, AUTH_OAUTH, or AUTH_REMOTE_USER, password management is delegated to the identity provider. This SIP does not modify those flows. However, a future SIP could improve the OIDC onboarding experience and documentation.
  • WebAuthn / Passkeys — Modern passwordless authentication. Requires significant frontend and backend work.

New or Changed Public Interfaces

REST API — New endpoints:

Method Endpoint Description
PUT /api/v1/me/password Authenticated user changes own password (requires current password)
POST /api/v1/auth/forgot-password Request a password reset email (unauthenticated)
POST /api/v1/auth/reset-password Complete password reset with token (unauthenticated)
PUT /api/v1/user/{id}/password Admin resets a user's password (Admin role required)
POST /api/v1/user/{id}/send-reset-link Admin triggers a reset email for a user (Admin role required)

REST API — Modified endpoints:

Method Endpoint Change
PUT /api/v1/user/{id} Email field becomes updatable by Admin role

CLI — New commands:

Command Description
superset change-email --username <user> --new-email <email> Update a user's email address
superset deactivate-user --username <user> Deactivate a user account

CLI — Existing commands (unchanged):

Command Description
superset fab reset-password --username <user> --password <pw> Reset password from CLI (already exists)
superset fab create-admin Create admin user (already exists)

Frontend — React components:

  • New ChangePasswordModal component (replaces legacy FAB SSR view)
  • New ForgotPasswordPage component (login page addition)
  • New ResetPasswordPage component (token-based reset)
  • Modified UserListModal to include admin password reset action
  • Password strength indicator component with real-time policy feedback

Configuration — New superset_config.py keys:

# Password policy
PASSWORD_MIN_LENGTH = 12
PASSWORD_REQUIRE_UPPERCASE = True
PASSWORD_REQUIRE_LOWERCASE = True
PASSWORD_REQUIRE_DIGIT = True
PASSWORD_REQUIRE_SPECIAL = True
PASSWORD_COMMON_LIST_CHECK = True

# Self-service reset
PASSWORD_RESET_ENABLED = False
PASSWORD_RESET_TOKEN_EXPIRY_MINUTES = 30
PASSWORD_RESET_RATE_LIMIT = "5 per 15 minutes"

# Login security
LOGIN_RATE_LIMIT = "10 per 5 minutes"
LOGIN_MAX_FAILURES = 5
LOGIN_LOCKOUT_DURATION_MINUTES = 15

Important: All features in this SIP apply exclusively when AUTH_TYPE = AUTH_DB. When external authentication providers are configured (OAuth, LDAP, REMOTE_USER), the password-related endpoints and UI elements are hidden.

New Dependencies

Package Purpose License Maintained
flask-limiter Rate limiting for login and reset endpoints MIT Yes (active)
argon2-cffi (optional) Alternative password hashing to bcrypt MIT Yes (active)

No new npm dependencies are anticipated. The frontend components will use existing Ant Design form components and Superset's design system.

Note: bcrypt is already a transitive dependency via Flask-AppBuilder. flask-limiter may already be available depending on the deployment; if not, it is lightweight and actively maintained.

Migration Plan and Compatibility

Database migrations:

  1. New table password_reset_token:

    • id (PK)
    • user_id (FK → ab_user.id)
    • token_hash (VARCHAR, indexed)
    • created_at (TIMESTAMP)
    • expires_at (TIMESTAMP)
    • used_at (TIMESTAMP, nullable)
  2. New table auth_audit_log:

    • id (PK)
    • user_id (FK → ab_user.id, nullable for failed logins with unknown user)
    • event_type (VARCHAR — e.g. login_success, login_failure, password_change, password_reset_request, password_reset_complete, email_change, account_lockout, account_deactivated)
    • ip_address (VARCHAR)
    • user_agent (TEXT)
    • metadata (JSON, nullable — additional context)
    • created_at (TIMESTAMP)
  3. New table account_lockout:

    • id (PK)
    • user_id (FK → ab_user.id)
    • failed_attempts (INTEGER)
    • locked_until (TIMESTAMP, nullable)
    • last_failed_at (TIMESTAMP)

    Note: on FAB schema ownership: The ab_* tables (e.g., ab_user, ab_role, ab_permission_view) are owned and managed by Flask-AppBuilder's own migration chain. Adding custom columns to these tables introduces a maintenance risk: FAB upstream may alter the same tables in future releases, causing Alembic migration conflicts, failed upgrades, or silent schema drift. To preserve clean upgrade paths and avoid coupling Superset's auth enhancements to FAB's release cycle, this SIP introduces new, Superset-managed tables (password_reset_token, auth_audit_log, account_lockout) that reference ab_user.id via foreign key without modifying the upstream schema. This approach also simplifies a potential future migration away from FAB's built-in user model.

Backward compatibility:

  • All new features are opt-in via configuration flags. Existing deployments are unaffected by default.
  • The existing superset fab reset-password CLI command remains unchanged.
  • The legacy FAB password change view is deprecated in favor of the new SPA-based component. During a transition period, the legacy route can be re-enabled via the config flag introduced in [PR #37773](fix(auth): gate legacy FAB password reset routes behind config flag #37773).
  • No breaking changes to existing stored URLs or bookmarks.

Rollout strategy:

  • Phase 1: Secure authenticated password change (Pillar 1) + audit logging foundation
  • Phase 2: Self-service forgot password flow (Pillar 2) + CLI email change
  • Phase 3: Admin UI password reset (Pillar 3)
  • Phase 4: Rate limiting and lockout (Pillar 4)

Rejected Alternatives

  1. Relying entirely on Flask-AppBuilder upstream — A PR ([dpgaspar/Flask-AppBuilder#1566](feat: Reset password email and 'Forgot password' solution dpgaspar/Flask-AppBuilder#1566)) was opened to add forgot-password to FAB itself, but has been stalled for years with no progress. Waiting for upstream is not viable given the community demand and the Superset project's migration to SPA architecture.

  2. Requiring all deployments to use OIDC/SAML — While external identity providers solve password management entirely, many small-to-medium deployments and development environments rely on AUTH_DB. Forcing migration to an external IdP would be a significant barrier to adoption.

  3. Direct database SQL for password resets — Some community workarounds suggest running UPDATE ab_user SET password = '...' directly. This is fragile, error-prone, bypasses all security controls, and should never be recommended as a supported workflow.

  4. Admin-only password resets (no self-service) — While simpler to implement, this still requires admin intervention for every forgotten password and does not scale for larger deployments. The self-service flow (Pillar 2) is essential for operational efficiency.

  5. Implementing a full identity management system — Building user lifecycle management, SSO, and identity federation within Superset itself would be overengineering. This SIP scopes the work to the minimum necessary for secure password management when using database authentication, while acknowledging that organizations with advanced requirements should use external identity providers.

Metadata

Metadata

Assignees

No one assigned

    Labels

    authenticationRelated to authenticationsipSuperset Improvement Proposal

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions