You'll get more opinionated answers, If I were doing I'll combine 3 & 4. Throw LoginFailedException
with an enum saying why.
void Login(string user, string password);//Or return a bool(redundant though)
class LoginFailedException : ApplicationException
{
public LoginFailReason Reason {get; private set;}
public LoginFailedException(LoginFailReason reason)
{
this.Reason = reason;
}
}
enum LoginFailReason
{
UnknownUser,
WrongPassword
}
Reason to choose exception option:
Assume you choose return value only approach, users of your api(may be client, or other developers) can get a chance to overlook the API.
instance.Login(user, password);
var accountInfo = instance.GetAccountInfo();//Assuming logged in; going to explode
Who knows they have to do like this
if(instance.Login(user, password) == LoginResult.Successful))
{
var accountInfo = instance.GetAccountInfo();
}
So, IMO throw exception in face saying I can't process your login request due to so and so reason. Make it simple.