* feat: add password reset flow using configured SMTP settings Implements password reset functionality for users with email/password login: Backend: - Add `/auth/request_password_reset` endpoint to request a password reset email - Add `/auth/reset_password` endpoint to reset password using token - Add `/auth/is_smtp_configured` endpoint to check if SMTP is available - Uses existing `magic_link` table for storing reset tokens - Tokens expire after 1 hour - Invalidates all existing sessions on password reset - Includes audit logging Frontend: - Add "Forgot password?" link on login page (shown when SMTP is configured) - Add `/user/forgot-password` page for requesting password reset - Add `/user/reset-password` page for entering new password - Both pages follow existing Windmill design patterns Security: - Always returns success response to prevent email enumeration - Password must be at least 8 characters - Uses argon2 for password hashing (same as existing login) Closes #7524 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> * nits * nits * fix oss * nits * fix oss --------- Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> Co-authored-by: HugoCasa <hugo@casademont.ch> Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
90 lines
2.2 KiB
Rust
90 lines
2.2 KiB
Rust
#[cfg(feature = "private")]
|
|
#[allow(unused)]
|
|
pub use crate::users_ee::*;
|
|
|
|
#[cfg(not(feature = "private"))]
|
|
use std::sync::Arc;
|
|
|
|
#[cfg(not(feature = "private"))]
|
|
use crate::db::ApiAuthed;
|
|
|
|
#[cfg(not(feature = "private"))]
|
|
use crate::users::{EditPassword, NewUser};
|
|
|
|
#[cfg(not(feature = "private"))]
|
|
use crate::{db::DB, webhook_util::WebhookShared};
|
|
|
|
#[cfg(not(feature = "private"))]
|
|
use argon2::Argon2;
|
|
|
|
#[cfg(not(feature = "private"))]
|
|
use axum::{extract::Extension, Json};
|
|
|
|
#[cfg(not(feature = "private"))]
|
|
use http::StatusCode;
|
|
|
|
#[cfg(not(feature = "private"))]
|
|
use serde::Deserialize;
|
|
|
|
#[cfg(not(feature = "private"))]
|
|
use windmill_common::error::{Error, Result};
|
|
|
|
#[cfg(not(feature = "private"))]
|
|
pub async fn create_user(
|
|
_authed: ApiAuthed,
|
|
_db: DB,
|
|
_webhook: WebhookShared,
|
|
_argon2: Arc<Argon2<'_>>,
|
|
mut _nu: NewUser,
|
|
) -> Result<(StatusCode, String)> {
|
|
Err(Error::internal_err(
|
|
"Not implemented in Windmill's Open Source repository".to_string(),
|
|
))
|
|
}
|
|
|
|
#[cfg(not(feature = "private"))]
|
|
pub async fn set_password(
|
|
_db: DB,
|
|
_argon2: Arc<Argon2<'_>>,
|
|
_authed: ApiAuthed,
|
|
_user_email: &str,
|
|
_ep: EditPassword,
|
|
) -> Result<String> {
|
|
Err(Error::internal_err(
|
|
"Not implemented in Windmill's Open Source repository".to_string(),
|
|
))
|
|
}
|
|
|
|
#[cfg(not(feature = "private"))]
|
|
pub fn hash_password(_argon2: Arc<Argon2<'_>>, _password: String) -> Result<String> {
|
|
Err(Error::internal_err(
|
|
"Not implemented in Windmill's Open Source repository".to_string(),
|
|
))
|
|
}
|
|
|
|
#[cfg(not(feature = "private"))]
|
|
pub fn send_email_if_possible(_subject: &str, _content: &str, _to: &str) {
|
|
tracing::warn!(
|
|
"send_email_if_possible is not implemented in Windmill's Open Source repository"
|
|
);
|
|
}
|
|
|
|
#[cfg(not(feature = "private"))]
|
|
#[derive(Deserialize, Debug)]
|
|
#[allow(dead_code)]
|
|
pub struct OnboardingData {
|
|
pub touch_point: String,
|
|
pub use_case: String,
|
|
}
|
|
|
|
#[cfg(not(feature = "private"))]
|
|
pub async fn submit_onboarding_data(
|
|
_authed: ApiAuthed,
|
|
Extension(_db): Extension<DB>,
|
|
Json(_data): Json<OnboardingData>,
|
|
) -> Result<String> {
|
|
Err(Error::internal_err(
|
|
"Not implemented in Windmill's Open Source repository".to_string(),
|
|
))
|
|
}
|