feat(passwordless): pass3: passwordless account (via emailed magic-link)
Backend
- RegisterDto — username and password both become Option<String> with #[serde(default)] so JSON can omit them entirely.
- AuthApplicationService::register — username uniqueness check skipped when None (multiple NULLs OK under the UNIQUE index); password hashing skipped when None; User::new called with the actual Options instead of forcing Some(...).
- auth_handler::register — branches on dto.password.is_none(). With password → existing 201 + UserDto. Without → triggers MagicLinkInviteService::send_login_link(&email) best-effort, then returns 200 + {"message": "Check your email…"}. The
OIDC-mode-disables-password-registration gate now only fires for the password path (email-only signup is still allowed even in OIDC-only mode, because it doesn't store a password).
- magic_link_handler::redirect_target — new 3-way decision tree:
- Resource target (folder invitation) → /#/files/folder/{id} (existing)
- NULL resource + is_external = false → /#/files (the welcome path for new internal users — they have a home folder)
- NULL resource + is_external = true → /#/sharedwithme (the existing external-user landing)
Tests
- New tests/api/registration.hurl with 9 requests covering: classic (with-password) register → 201 + UserDto, email-only register → 200 + uniform message + welcome magic-link captured, redemption → 302 to /#/files + cookies set, profile read → username
absent + is_external: false, resend magic-link works (eligible while passwordless), cleanup deletes both new users.
- Wired into tests/api/run.sh right after auth_login.hurl.
Plan additions
- auth-simplification.md gained PR 22 at the bottom of the PR sequence — device-bound magic-link redemption via challenge cookie + asymmetric TTLs (login: 10 min, invitation: 24 h). Full design recap, schema migration, config knobs
(OXICLOUD_MAGIC_LINK_LOGIN_TTL_MINUTES / _INVITE_TTL_HOURS), and Hurl coverage outline are in the plan. Slots in before PR 21's docs so the architecture page describes the final state from the start.
Checks — cargo fmt, cargo clippy --all-features --all-targets -- -D warnings, cargo test --lib (297 passed), biome, stylelint, tsc, full Hurl suite (16 files) all green.
This commit is contained in:
@@ -80,9 +80,21 @@ pub struct LoginDto {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
|
||||
pub struct RegisterDto {
|
||||
pub username: String,
|
||||
/// Optional handle (2-64 chars, no `@`). When omitted, the user can
|
||||
/// claim one later via the profile-edit endpoint. Users without a
|
||||
/// username cannot use NextCloud clients or create app passwords
|
||||
/// (Basic-Auth resolves users by username); web UI / native API
|
||||
/// works fine without one.
|
||||
#[serde(default)]
|
||||
pub username: Option<String>,
|
||||
pub email: String,
|
||||
pub password: String,
|
||||
/// Optional password (≥8 chars when present). When omitted, a
|
||||
/// welcome magic-link is mailed to `email` for first-session
|
||||
/// bootstrap. The user can later set a password via the
|
||||
/// change-password endpoint to switch to classic username/email +
|
||||
/// password login.
|
||||
#[serde(default)]
|
||||
pub password: Option<String>,
|
||||
}
|
||||
|
||||
/// DTO for the one-time initial admin setup endpoint (`/api/setup`).
|
||||
|
||||
Reference in New Issue
Block a user