The first accounts in this project's history with no password. Several things that were true stop being true, and one of them is a check already written and waiting.
Scope
Let a social-only customer set a password. One route, not two. Setting the first password takes no current password because there is none to give, and the customer is already authenticated by their session; changing an existing one keeps demanding the current one. Branch on whether the hash is null — two endpoints would be two places to get the guard wrong, and the one that gets forgotten is whichever is not exercised by hand.
Fix the three routes that compare against a hash — login, change password, the email change. Each becomes a case rather than a failure, and the message says the real thing: this account has no password, not that the password was wrong.
Show the customer what they have. On the account page, next to the passkeys: password set or not, Google connected or not. A customer cannot manage credentials they cannot see, which is the argument that made the passkey list worth building.
The check that finally fires
DELETE /api/customers/me/passkeys/:id already refuses to remove a customer's only way in. It was written against the condition rather than the schema and has been unreachable since #40, because password_hash was NOT NULL. This is the phase where it starts running, so it needs a test that actually exercises it: a customer with no password and one passkey, whose revocation is refused.
Careful with login
Answering "this account has no password" to any submitted address turns the login form into an oracle for which customers use Google. Keep the single existing refusal, and let the account page be where a signed-in customer learns what they have.
Worth a second look
Password reset already sets a hash and does not care whether one was there before, so a social-only customer requesting a reset gets a working password. That is reasonable and needs a test to pin it. What it also does, since #42, is remove every passkey — and a social-only customer resetting a password they never had is a different situation from a lockout. If that removal reads wrong in this case, better found now.
Done when
A Google-only customer can set a password, delete their account, change their address, and cannot revoke their last way in; and the login form still refuses everything identically.
Part of #332. Phase 5 of six. Depends on #342.
The first accounts in this project's history with no password. Several things that were true stop being true, and one of them is a check already written and waiting.
## Scope
- **Let a social-only customer set a password.** One route, not two. Setting the first password takes no current password because there is none to give, and the customer is already authenticated by their session; changing an existing one keeps demanding the current one. Branch on whether the hash is null — two endpoints would be two places to get the guard wrong, and the one that gets forgotten is whichever is not exercised by hand.
- **Fix the three routes that compare against a hash** — login, change password, the email change. Each becomes a case rather than a failure, and the message says the real thing: this account has no password, not that the password was wrong.
- **Show the customer what they have.** On the account page, next to the passkeys: password set or not, Google connected or not. A customer cannot manage credentials they cannot see, which is the argument that made the passkey list worth building.
## The check that finally fires
`DELETE /api/customers/me/passkeys/:id` already refuses to remove a customer's only way in. It was written against the condition rather than the schema and has been unreachable since #40, because `password_hash` was `NOT NULL`. This is the phase where it starts running, so it needs a test that actually exercises it: a customer with no password and one passkey, whose revocation is refused.
## Careful with login
Answering "this account has no password" to any submitted address turns the login form into an oracle for which customers use Google. Keep the single existing refusal, and let the account page be where a signed-in customer learns what they have.
## Worth a second look
Password reset already sets a hash and does not care whether one was there before, so a social-only customer requesting a reset gets a working password. That is reasonable and needs a test to pin it. What it also does, since #42, is remove every passkey — and a social-only customer resetting a password they never had is a different situation from a lockout. If that removal reads wrong in this case, better found now.
## Done when
A Google-only customer can set a password, delete their account, change their address, and cannot revoke their last way in; and the login form still refuses everything identically.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Part of #332. Phase 5 of six. Depends on #342.
The first accounts in this project's history with no password. Several things that were true stop being true, and one of them is a check already written and waiting.
Scope
The check that finally fires
DELETE /api/customers/me/passkeys/:idalready refuses to remove a customer's only way in. It was written against the condition rather than the schema and has been unreachable since #40, becausepassword_hashwasNOT NULL. This is the phase where it starts running, so it needs a test that actually exercises it: a customer with no password and one passkey, whose revocation is refused.Careful with login
Answering "this account has no password" to any submitted address turns the login form into an oracle for which customers use Google. Keep the single existing refusal, and let the account page be where a signed-in customer learns what they have.
Worth a second look
Password reset already sets a hash and does not care whether one was there before, so a social-only customer requesting a reset gets a working password. That is reasonable and needs a test to pin it. What it also does, since #42, is remove every passkey — and a social-only customer resetting a password they never had is a different situation from a lockout. If that removal reads wrong in this case, better found now.
Done when
A Google-only customer can set a password, delete their account, change their address, and cannot revoke their last way in; and the login form still refuses everything identically.