The salt is random for each different user/password combination. It's not random for the same user/password combination. If that would be the case, then you would not be able to verify the password, as you already found out. If a large enough salt is generated with a sufficiently secure random number generator then it would even be impossible to verify the password. The idea of the salt is to protect against rainbow table attacks as well as creating a different password hash for different users if the password is the same.
The salt is usually stored together with the username and password hash in the database. It could be made part of a special construct that contains the salt and the password hash or it could be stored in a separate column. Sometimes the password-hash is actually a special string containing both the salt and the hash in some kind of format (using hexadecimal or base64 encoding) that needs to be parsed, but it could also a binary value simply consisting of a statically sized salt and statically sized hash.
An example bcrypt string would be:
$2a$12$QyrjMQfjgGIb4ymtdKQXIewDBqhA3eNppF8qOrMhidnEbzNvmHqhy
which is constructed as in this SO answer.
Setup:
- find user
- receive & verify old + password (see below)
- receive new password
- generate random salt
- calculate hash from password and salt
- store salt & hash in database with user
Verification:
- find user
- receive password
- retrieve salt & hash for user
- calculate hash to verify from password and salt
- compare and return result
Usually, for security reasons, you should try and do a time-constant compare, even if that's not really an issue for password hashing. Furthermore often no distinction is made between unknown user and wrong password, simply to avoid giving information to attackers.
It makes sense to construct your password hashing scheme in such a way that it allows for updates to the amount of iterations, hash size, hash function etc.