Email as a UserName

It seems there is an issue with email as a username. I am currently converting an existing userbase to SS auth repository.
I am already using IsValidUsernameFn = s => IsValidUsernameFn(s) where I always return true just to test this implementation. I can create a user with GetAuthRepository().CreateUserAuth(user1, “xxxxx”); but when I try to authenticate with the email address as a the userName with correct password, I always get a not authenticated.
Any idead?

It’s hard to be able to tell what the issue is without the code you’re using to create the User or Exception details. We need to see what you’re trying so any context that shows what you’re doing and what the issue is will help. Ideally we’d want as much info to be able to repro the issue ourselves.

The UserAuth Repository will look at the Email field if Username contains an @ so the Users Email should be stored in the Email field in the UserAuth table, but it’s not clear that’s what you’re doing.

Ok, adding the email address to the email worked! Thanks.
But I still have an error with my custom provider.

I inherit from CredentialsAuthProvider then I implement:

       public override bool TryAuthenticate(IServiceBase authService, string userName, string password)

In this I do the following:

        var defaultResult = base.TryAuthenticate(authService, userName, password);
        if (defaultResult) return true;
        var authRepository = ServiceStackHost.Instance.GetAuthRepository();

I then check another DB for the userName, Password combination. If I find this there, I create a new user:

                            userAuth = new UserAuth
                            {
                                UserName = administrator.Login,
                                DisplayName = administrator.Name,
                                Email = administrator.Login
                            };
                            userAuth.Roles.Add("Admin");
                            userAuth.Permissions.Add("DBID|" + administrator.DatabaseId);
                            authRepository.CreateUserAuth(userAuth, administrator.Password);

then return true.

The user is created in the DB, but the POST is giving me an error:
400 Bad Request

{
  "ResponseStatus": {
    "ErrorCode": "ArgumentNullException",
    "Message": "Value cannot be null.\r\nParameter name: String",
    "StackTrace": "[Authenticate: 18/02/2017 15:10:14]:\n[REQUEST: {provider:credentials,UserName:krzysztof.gora@cogno.eu,Password:[redated],UseTokenCookie:True}]\nSystem.ArgumentNullException: Value cannot be null.\r\nParameter name: String\r\n   at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)\r\n   at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)\r\n   at lambda_method(Closure , Object , List`1 )\r\n   at ServiceStack.ExpressionUtil.CachedExpressionCompiler.Compiler`2.<>c__DisplayClass7_1.<CompileFromFingerprint>b__1(TIn model)\r\n   at ServiceStack.CachedExpressionCompiler.Evaluate(Expression arg)\r\n   at ServiceStack.OrmLite.SqlExpression`1.VisitMethodCall(MethodCallExpression m)\r\n   at ServiceStack.OrmLite.SqlExpression`1.Visit(Expression exp)\r\n   at ServiceStack.OrmLite.SqlExpression`1.VisitBinary(BinaryExpression b)\r\n   at ServiceStack.OrmLite.SqlExpression`1.Visit(Expression exp)\r\n   at ServiceStack.OrmLite.SqlExpression`1.VisitBinary(BinaryExpression b)\r\n   at ServiceStack.OrmLite.SqlExpression`1.Visit(Expression exp)\r\n   at ServiceStack.OrmLite.SqlExpression`1.VisitLambda(LambdaExpression lambda)\r\n   at ServiceStack.OrmLite.SqlExpression`1.Visit(Expression exp)\r\n   at ServiceStack.OrmLite.SqlExpression`1.AppendToWhere(String condition, Expression predicate)\r\n   at ServiceStack.OrmLite.SqlExpression`1.Where(Expression`1 predicate)\r\n   at ServiceStack.OrmLite.ReadExpressionCommandExtensions.Select[T](IDbCommand dbCmd, Expression`1 predicate)\r\n   at ServiceStack.OrmLite.OrmLiteReadExpressionsApi.<>c__DisplayClass14_0`1.<Select>b__0(IDbCommand dbCmd)\r\n   at ServiceStack.OrmLite.OrmLiteExecFilter.Exec[T](IDbConnection dbConn, Func`2 filter)\r\n   at ServiceStack.OrmLite.OrmLiteReadExpressionsApi.Exec[T](IDbConnection dbConn, Func`2 filter)\r\n   at ServiceStack.OrmLite.OrmLiteReadExpressionsApi.Select[T](IDbConnection dbConn, Expression`1 predicate)\r\n   at ServiceStack.Auth.OrmLiteAuthRepositoryBase`2.<>c__DisplayClass28_0.<GetRoles>b__0(IDbConnection db)\r\n   at ServiceStack.Auth.OrmLiteAuthRepository`2.Exec[T](Func`2 fn)\r\n   at ServiceStack.Auth.OrmLiteAuthRepositoryBase`2.GetRoles(String userAuthId)\r\n   at ServiceStack.Auth.JwtAuthProvider.Execute(IServiceBase authService, IAuthProvider authProvider, IAuthSession session, AuthenticateResponse response)\r\n   at ServiceStack.Auth.AuthenticateService.Post(Authenticate request)\r\n   at lambda_method(Closure , Object , Object )\r\n   at ServiceStack.Host.ServiceRunner`1.Execute(IRequest request, Object instance, TRequest requestDto)",
    "Errors": [
      {
        "ErrorCode": "ArgumentNullException",
        "FieldName": "String",
        "Message": "Value cannot be null.\r\n"
      }
    ]
  }
}

Any idea what is going wrong, or I can debug further?

It wont have an impact here, but you should pass in the IRequest when retrieving the User Auth Repository, e.g:

var authRepo = (IUserAuthRepository)HostContext.AppHost.GetAuthRepository(authService.Request);

You should also use the explicit API when adding Users roles/permissions, e.g:

userAuth = new UserAuth
{
    UserName = administrator.Login,
    DisplayName = administrator.Name,
    Email = administrator.Login
};
userAuth = authRepository.CreateUserAuth(userAuth, administrator.Password);

authRepo.AssignRoles(userAuth, 
    roles: new[]{ "Admin" }, 
    permissions: new[]{ "DBID|" + administrator.DatabaseId });

The error is a null ref when calling GetRoles() on the User Auth Repo so using the proper APIs might fix it.

Hi still no luck. I did change everything as said. But I still get the same error.
The user is created with the correct roles and permissions.

userAuth = new UserAuth
{
    UserName = administrator.Login,
    DisplayName = administrator.Name,
    Email = administrator.Login
};
userAuth = authRepository.CreateUserAuth(userAuth, administrator.Password);

authRepository.AssignRoles(userAuth, 
    roles: new[] { "Admin" }, 
    permissions:new []
    {
        "DBID|" + administrator.DatabaseId,
        "legacy-administrator-id|" + administrator.Id
    });

from the stacktrace it seems that a numeric field is not filled in for some reason.

ok in that case I can’t tell what the issue is from here, if you can put together a stand-alone project (e.g. on Github) that I can run that shows the issue I’ll be able to identify what’s causing it.

Hi please find [Sample files][1] to test the issue
[1]: https://bitbucket.org/snippets/stefandevo/MeyqA

I then use Postman

POST /authenticate HTTP/1.1
Host: localhost:59742
Content-Type: application/json

{"provider":"credentials","userName":"user1@host.com", "password":"p@55word", "useTokenCookie": "true"}

After I get

{
  "ResponseStatus": {
    "ErrorCode": "ArgumentNullException",
    "Message": "Value cannot be null.\r\nParameter name: String",
    "StackTrace": "[Authenticate: 19/02/2017 11:23:40]:\n[REQUEST: {provider:credentials,UserName:user1@host.com,Password:p@55word,UseTokenCookie:True}]\nSystem.ArgumentNullException: Value cannot be null.\r\nParameter name: String\r\n   at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)\r\n   at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)\r\n   at lambda_method(Closure , Object , List`1 )\r\n   at ServiceStack.ExpressionUtil.CachedExpressionCompiler.Compiler`2.<>c__DisplayClass7_1.<CompileFromFingerprint>b__1(TIn model)\r\n   at ServiceStack.CachedExpressionCompiler.Evaluate(Expression arg)\r\n   at ServiceStack.OrmLite.SqlExpression`1.VisitMethodCall(MethodCallExpression m)\r\n   at ServiceStack.OrmLite.SqlExpression`1.Visit(Expression exp)\r\n   at ServiceStack.OrmLite.SqlExpression`1.VisitBinary(BinaryExpression b)\r\n   at ServiceStack.OrmLite.SqlExpression`1.Visit(Expression exp)\r\n   at ServiceStack.OrmLite.SqlExpression`1.VisitBinary(BinaryExpression b)\r\n   at ServiceStack.OrmLite.SqlExpression`1.Visit(Expression exp)\r\n   at ServiceStack.OrmLite.SqlExpression`1.VisitLambda(LambdaExpression lambda)\r\n   at ServiceStack.OrmLite.SqlExpression`1.Visit(Expression exp)\r\n   at ServiceStack.OrmLite.SqlExpression`1.AppendToWhere(String condition, Expression predicate)\r\n   at ServiceStack.OrmLite.SqlExpression`1.Where(Expression`1 predicate)\r\n   at ServiceStack.OrmLite.ReadExpressionCommandExtensions.Select[T](IDbCommand dbCmd, Expression`1 predicate)\r\n   at ServiceStack.OrmLite.OrmLiteReadExpressionsApi.<>c__DisplayClass14_0`1.<Select>b__0(IDbCommand dbCmd)\r\n   at ServiceStack.OrmLite.OrmLiteExecFilter.Exec[T](IDbConnection dbConn, Func`2 filter)\r\n   at ServiceStack.OrmLite.OrmLiteReadExpressionsApi.Exec[T](IDbConnection dbConn, Func`2 filter)\r\n   at ServiceStack.OrmLite.OrmLiteReadExpressionsApi.Select[T](IDbConnection dbConn, Expression`1 predicate)\r\n   at ServiceStack.Auth.OrmLiteAuthRepositoryBase`2.<>c__DisplayClass28_0.<GetRoles>b__0(IDbConnection db)\r\n   at ServiceStack.Auth.OrmLiteAuthRepository`2.Exec[T](Func`2 fn)\r\n   at ServiceStack.Auth.OrmLiteAuthRepositoryBase`2.GetRoles(String userAuthId)\r\n   at ServiceStack.Auth.JwtAuthProvider.Execute(IServiceBase authService, IAuthProvider authProvider, IAuthSession session, AuthenticateResponse response)\r\n   at ServiceStack.Auth.AuthenticateService.Post(Authenticate request)\r\n   at lambda_method(Closure , Object , Object )\r\n   at ServiceStack.Host.ServiceRunner`1.Execute(IRequest request, Object instance, TRequest requestDto)",
    "Errors": [
      {
        "ErrorCode": "ArgumentNullException",
        "FieldName": "String",
        "Message": "Value cannot be null.\r\n"
      }
    ]
  }
}

The issue is because session.UserAuthId is null. It’s null because you’re creating a new User in your custom TryAuthenticate method so to the successful code path of TryAuthenticate in CredentialsAuthProvider is never run for the new user which doesn’t have a chance populate the Users session.

If you change it so you call TryAuthenticate again after creating the user it will work, e.g:

public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
    var defaultResult = base.TryAuthenticate(authService, userName, password);
    if (defaultResult) return true;

    var authRepository = (IUserAuthRepository)HostContext.AppHost.GetAuthRepository(authService.Request);

    var administratorFound = _legacyUsers.Where(u => u.UserName == userName && u.Password == password).ToList();
    if (administratorFound.Count == 0) return false;

    var administrator = administratorFound.First();

    IUserAuth userAuth = new UserAuth
    {
        UserName = administrator.UserName,
        Email = administrator.UserName
    };
    userAuth = authRepository.CreateUserAuth(userAuth, administrator.Password);
    AssignRoleAndPermissionsForAdmistrator(authRepository, userAuth, administrator);

    return base.TryAuthenticate(authService, userName, password);
}

Ok thanks for this. Great support!

1 Like