While implementing Google OAuth link/unlink (docs/adr/0028), the design called for blocking DELETE /api/auth/google with a 409 if unlinking would leave the account with neither a password nor a Google link (agreed in the grill-with-docs interview: "block the unlink with a 409 if password_hash is also null").
In practice users.password_hash is NOT NULL today and every user is created via /api/setup or the env-var migration, both of which always set a password. Every OAuth-created user this release only exists via link-only flow (see #92 for the eventual OAuth-only signup path) — so the guard is currently unreachable/dead code, and was intentionally left out of the initial implementation rather than shipped as untestable dead code.
Once #92 (invite-based OAuth-only signup) lands and password_hash actually can be null:
- migrate password_hash to nullable (SQLite needs a table-rebuild migration, ALTER COLUMN isn't supported)
- add the "no other credential" check back into the unlink handler in src/google_oauth.rs
While implementing Google OAuth link/unlink (docs/adr/0028), the design called for blocking DELETE /api/auth/google with a 409 if unlinking would leave the account with neither a password nor a Google link (agreed in the grill-with-docs interview: "block the unlink with a 409 if password_hash is also null").
In practice
users.password_hashisNOT NULLtoday and every user is created via /api/setup or the env-var migration, both of which always set a password. Every OAuth-created user this release only exists via link-only flow (see #92 for the eventual OAuth-only signup path) — so the guard is currently unreachable/dead code, and was intentionally left out of the initial implementation rather than shipped as untestable dead code.Once #92 (invite-based OAuth-only signup) lands and password_hash actually can be null: