RedisAuthRepository not working

I use RedisAuthRepository as user auth repository in my .NET Core 2.0 app (ServiceStack 5.0.3), but I couldn’t read already stored UserAuth objects from my Redis db with GetUserAuthByUserName(UserName) the result is always null?

Stored object in redis:

Any queries in Redis requires looking up an Index, for the UserName it tries looking it up from the hash:UserAuth:UserName>UserId index which is stored in a hash that’s maintained when users are added in the Repositories CreateUserAuth() or UpdateUserAuth() APIs. So I’d first check to make sure the UserName/UserId mapping exists in the hash:UserAuth:UserName>UserId hash.

Hello Demis,

thank you for the quick reply …yes the hashes exist.

Ok so then it should work, can you provide a standalone repro showing the code you’re using which doesn’t work.

AppHost.Configure():

var redisHost = AppSettings.Get<string>("CacheConnectionString");
...
container.Register<IRedisClientsManager>(new RedisManagerPool(redisHost));
...
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
		new IAuthProvider[] {
			new CredentialsAuthProvider(AppSettings),
			new BasicAuthProvider(AppSettings),
			new JwtAuthProvider(AppSettings),
		})
		{
			IncludeRegistrationService = true,
			MaxLoginAttempts = AppSettings.Get("MaxLoginAttempts", 5),
		});

var redis = container.Resolve<IRedisClientsManager>();


var userRep = new RedisAuthRepository(redis);
container.Register<IUserAuthRepository>(userRep);

string hash;
string salt;

new SaltedHash().GetHashAndSaltString("blabla", out hash, out salt);

UserAuth user = new UserAuth
{
	DisplayName = "ssePosFeed-DisplayName",
	Email = "ssePosFeed@if.com",
	UserName = "ssePosFeed",
	FirstName = "ssePosFeed-FirstName",
	LastName = "ssePosFeed-LastName",
	PasswordHash = hash,
	Salt = salt,
	Roles = new List<string>() { "SSEPosFeed" },
	Permissions = new List<string>() { "GetPosFeed" }
};
  // Create user
  userRep.CreateUserAuth(user, "blabla");

//userExist is always null
var userExist = userRep.GetUserAuthByUserName(user.UserName);
...

appsettings.json:

...
"CacheConnectionString": "Q/4v4D...=@...cache.redis.cache.windows.net:6380?ssl=true&amp;idletimeoutsecs=40",
"OperatorVersion": "0.1",
"jwt.HashAlgorithm": "RS256",
"jwt.AllowInFormData": true,
"jwt.AllowInQueryString": true,
"jwt.ExpireTokensInDays": 30,
"jwt.SetBearerTokenOnAuthenticateResponse": true,
"jwt.PrivateKeyXml": "<RSAKeyValue><Modulus>0WQPi....</D></RSAKeyValue>",
"jwt.PublicKeyXml": "<RSAKeyValue><Modulus>0WQPi...</Exponent></RSAKeyValue>"
...

I’m not able to repro this error which is working as expected. I’ve distilled it into the smallest example with minimal deps below.

One issue with your example is that you shouldn’t manually populate the PasswordHash/Salt as they are automatically populated by CreateUserAuth(). Also note that from v5.0.0 ServiceStack uses ASP.NET Identity v3’s PBKDF2 Password Hashing implementation instead of SaltedHash.

Otherwise this example works in both .NET Framework and .NET Core:

using (new BasicAppHost().Init())
{
    var redisManager = new RedisManagerPool(redisHost);

    var userRep = new RedisAuthRepository(redisManager);

    var user = new UserAuth
    {
        DisplayName = "ssePosFeed-DisplayName",
        Email = "ssePosFeed@if.com",
        UserName = "ssePosFeed",
        FirstName = "ssePosFeed-FirstName",
        LastName = "ssePosFeed-LastName",
        Roles = new List<string> { "SSEPosFeed" },
        Permissions = new List<string> { "GetPosFeed" }
    };

    // Create user
    userRep.CreateUserAuth(user, "blabla");

    //userExist is always null
    var userExist = userRep.GetUserAuthByUserName(user.UserName);

    userExist.PrintDump();
}

This is the output from .NET Core:

{
	__type: "ServiceStack.Auth.UserAuth, ServiceStack",
	id: 1,
	userName: ssePosFeed,
	email: ssePosFeed@if.com,
	firstName: ssePosFeed-FirstName,
	lastName: ssePosFeed-LastName,
	displayName: ssePosFeed-DisplayName,
	passwordHash: AQAAAAEAACcQAAAAENTLmpuZtrdhJmbpdgOXmcaJOD+ur5fymi4Tn6C61BV+DIKjCXHOUyzVPk13YH41Kg==,
	roles: 
	[
		SSEPosFeed
	],
	permissions: 
	[
		GetPosFeed
	],
	createdDate: 2018-02-03T16:25:32.2180000Z,
	modifiedDate: 2018-02-03T16:25:32.2180000Z,
	invalidLoginAttempts: 0
}

If the user already exists you’ll need to clear your cache.

What do you get when you run it?

ok this example runs? But what is the difference?

There isn’t one, this same example also works in a .NET Core 2.0 or .NET Framework AppHost:

class AppHost : AppSelfHostBase 
{
    public AppHost()  : base(nameof(AppHost), typeof(AppHost).Assembly) {}
    public override void Configure(Container container)
    {
        var redisManager = new RedisManagerPool("localhost");
        using (var r = redisManager.GetClient()) { r.FlushAll(); } //clear cache

        var userRep = new RedisAuthRepository(redisManager);
        var user = new UserAuth {
            DisplayName = "ssePosFeed-DisplayName",
            Email = "ssePosFeed@if.com",
            UserName = "ssePosFeed",
            FirstName = "ssePosFeed-FirstName",
            LastName = "ssePosFeed-LastName",
            Roles = new List<string> { "SSEPosFeed" },
            Permissions = new List<string> { "GetPosFeed" }
        };

        userRep.CreateUserAuth(user, "blabla");

        //userExist is always null
        var userExist = userRep.GetUserAuthByUserName(user.UserName);
        userExist.PrintDump();
    }
}

Which also prints out the user when the AppHost is started:

var appHost = new AppHost()
    .Init()
    .Start(Config.ListeningOn);

Uses AppSelfHostBase .NET Core AppHost wrapper from ServiceStack.Kestrel

If you can put together a stand-alone repro I can run locally (e.g. on GitHub) I can investigate further.

thank you I found out the problem the __type was missed in the redis stored object…if I commented the line:

//JsConfig.ExcludeTypeInfo = true;

it run’s but is there another solution because if my MessageObject contains a property of type JsonObject so I won’t return the __type field therefore I have disabled it with JsConfig.ExcludeTypeInfo = true?

Excluding the TypeInfo is going to break deserializing anything that requires the __type info property (e.g. Auth Repos), which is only emitted when it’s needed.

See the docs on Customizing JSON for alt options for customizing JSON responses.

Ok thanks for this information.