feat: self-service password reset via email (Phase 4)
- email column on users (migration-safe ALTER TABLE) - email_reset_tokens table (1h TTL, single-use) - smtp.py: send via STARTTLS, config from CLI/env vars - POST /api/auth/request-reset — sends reset link, always 200 (no email leak) - POST /api/auth/reset-password-token — consumes email token - GET/POST /api/me/email — users can register/update their email - reset-password page: email form primary, admin code form as toggle, token form shown automatically when ?token= is in URL - CLI: --smtp-host/port/user/password/from (BINCIO_SMTP_* env vars)
This commit is contained in:
@@ -98,6 +98,11 @@ def show_secret_cmd(data_dir: str) -> None:
|
||||
type=click.Path(), help="Path to RS256 PEM private key. Enables OIDC endpoints.")
|
||||
@click.option("--oidc-issuer", default=None, envvar="BINCIO_OIDC_ISSUER",
|
||||
help="OIDC issuer URL (e.g. https://bincio.org). Required when --oidc-private-key-file is set.")
|
||||
@click.option("--smtp-host", default=None, envvar="BINCIO_SMTP_HOST", help="SMTP server hostname.")
|
||||
@click.option("--smtp-port", default=587, envvar="BINCIO_SMTP_PORT", type=int, show_default=True)
|
||||
@click.option("--smtp-user", default=None, envvar="BINCIO_SMTP_USER", help="SMTP login username.")
|
||||
@click.option("--smtp-password", default=None, envvar="BINCIO_SMTP_PASSWORD", help="SMTP password / app password.")
|
||||
@click.option("--smtp-from", default=None, envvar="BINCIO_SMTP_FROM", help="From address (defaults to --smtp-user).")
|
||||
def serve_cmd(
|
||||
data_dir: str,
|
||||
host: str,
|
||||
@@ -105,6 +110,11 @@ def serve_cmd(
|
||||
jwt_secret: str | None,
|
||||
oidc_private_key_file: str | None,
|
||||
oidc_issuer: str | None,
|
||||
smtp_host: str | None,
|
||||
smtp_port: int,
|
||||
smtp_user: str | None,
|
||||
smtp_password: str | None,
|
||||
smtp_from: str | None,
|
||||
) -> None:
|
||||
"""Start the bincio-auth API server."""
|
||||
import uvicorn
|
||||
@@ -140,11 +150,21 @@ def serve_cmd(
|
||||
if not deps.oidc_issuer:
|
||||
raise click.UsageError("--oidc-issuer is required when --oidc-private-key-file is set")
|
||||
|
||||
if smtp_host and smtp_user and smtp_password:
|
||||
from bincio.auth import smtp as _smtp
|
||||
_smtp.host = smtp_host
|
||||
_smtp.port = smtp_port
|
||||
_smtp.user = smtp_user
|
||||
_smtp.password = smtp_password
|
||||
_smtp.sender = smtp_from or smtp_user
|
||||
|
||||
console.print("[bold]bincio-auth[/bold]")
|
||||
console.print(f" Data: [cyan]{dd}[/cyan]")
|
||||
console.print(f" URL: [cyan]http://{host}:{port}[/cyan]")
|
||||
if deps.oidc_issuer:
|
||||
console.print(f" OIDC: [cyan]{deps.oidc_issuer}[/cyan]")
|
||||
if smtp_host:
|
||||
console.print(f" SMTP: [cyan]{smtp_user}[/cyan] via [cyan]{smtp_host}:{smtp_port}[/cyan]")
|
||||
console.print()
|
||||
|
||||
uvicorn.run(srv.app, host=host, port=port, log_level="info")
|
||||
|
||||
Reference in New Issue
Block a user