What's your motivation for slow-hashing on the client-side?
- it doesn't provide any extra security, the credential(s) (password, hash of the password, salt derived from password which will be used for hashing the password, whatever...) will still need to be transmitted to the server - there's no way to avoid that
Keep it simple - you're much less likely to introduce some sort of vulnerability into your system. The following protocol is pretty standard:
- send the client's username/password to the server over TLS/SSL (it's worth the effort, really)
- slow-hash the client's password with either Scrypt (preferable), or PBKDF2-SHA256 (at least 100'000 iterations) and a user-specific salt (stored in the DB with the hash)
- for each login attempt, compare the output of the above with the hash you have stored in the DB
Note: Bcrypt is fine, some people fetishize it, others prefer PBKDF2 because it's more 'standard' (and it's output length is configurable). Scrypt is the state-of-the-art, although it's less widely used, and can be more complicated to implement.
After further consideration, and better understanding exactly what you're asking (I think), I'm going to say yes: what you're proposing is fine, provided certain conditions are met. The issue that @StephenTouset pointed out is valid, and has to be avoided. However, if I understand your question correctly, then you're already mitigating that weakness. Consider the following:
Client slow-hashes password with PBKDF2 or Scrypt (use of email address or username as salt is questionable - email addresses change and usernames could be used in dictionary attacks). This initial slow-hashing mitigates dictionary attacks, and offloads the heavy work from the server to the client
Client submits the output of the slow-hash (256 bits) to the server (over TLS/SSL), where it is then hashed once with SHA-256 (no salt necessary) and compared with the hash stored in the database.
Should the database be compromised, an attacker would still need to brute-force the hash of a 256-bit value (the client-side slow-hash output) to verify a correct guess at the password, pretty much rendering the hashes stored in the database useless to an attacker.
I think the theory is fine. In practise, I think it's a bit messy and maybe unwarranted (your comment mentioned up to 1000 login attempts per second, are you really anticipating up to 86 million logins per day??). Only you can decide whether you can justify it.