1

I refer to this question. I try to create a server account if it does not exist when the server starts. I have overwritten OnAfterInit to do this. Unfortunately no keys are created and worse, my ApiKeyAuthProvider seems to be null! Here is the code:

public class AppHost : AppHostHttpListenerBase
{
    private PooledRedisClientManager RedisBusPool { get; set; }

    public override void Configure(Container container)
    {
        RedisBusPool = new PooledRedisClientManager(connStrBus);

        #region IOC registrations
        container.Register<IRedisClientsManager>(c => new PooledRedisClientManager(connStrBus));
        container.Register(c => new RedisAuthRepository(RedisBusPool));
        //...
        #endregion


        #region ServiceStack plugins
        Plugins.Add(new AuthFeature(() => new AuthUserSession(), 
            new IAuthProvider[] {
                new BediCredentialsAuthProvider(),
                new ApiKeyAuthProvider(AppSettings)
                {
                    KeyTypes = new []{"secret", "publishable"},
                },
            }
        ));
        //...

        #endregion
        //...
    }

    public override void OnAfterInit()
    {
        #region Add test users
        var authRepo = TryResolve<RedisAuthRepository>();
        var user = authRepo.GetUserAuthByUserName("JoeTest");
        if (user == null)
        {
            var newUser = new UserAuth();
            newUser.UserName = "JoeTest";
            newUser.Email = "Joe@test.com";
            newUser.FirstName = "Joe";
            newUser.LastName = "Test";
            user = authRepo.CreateUserAuth(newUser, "topSecret!");
        }

        var keys = authRepo.GetUserApiKeys(user.Id.ToString());
        foreach (var apiKey in keys)
        {
            Debug.WriteLine(apiKey);
        }

        //CANT GET THE AUTHPROVIDER IT IS ALWAYS NULL HERE
        var authProvider = (ApiKeyAuthProvider)AuthenticateService.GetAuthProvider(ApiKeyAuthProvider.Name);


        #endregion
        base.OnAfterInit();
    }

}

The user is successfully created with password, salt etc. Here is what my Redis database shows:

{
  "__type": "ServiceStack.Auth.UserAuth, ServiceStack",
  "Id": 2,
  "UserName": "JoeTest",
  "Email": "Joe@test.com",
  "FirstName": "Joe",
  "LastName": "Test",
  "Salt": "1/696g==",
  "PasswordHash": "FZNI5cR5TWTpbvvm9PDy/w/JdRZImQYU5m5P6z8j7TY=",
  "DigestHa1Hash": "e4e0cff88386c6f991d13a3802ce7b53",
  "Roles": [],
  "Permissions": [],
  "CreatedDate": "\/Date(1491811177540)\/",
  "ModifiedDate": "\/Date(1491811177540)\/",
  "InvalidLoginAttempts": 0
}

Questions:

  1. How are the API key created? What am I missing or does this only work with the RegistrationFeatureenabled? If so, how can I protect this (self-registration is not allowed in my scenario) and how can I use it after the initialization of my service?
  2. I was browsing a bit through the code of RedisAuthRepository.cs and found this:

    var saltedHash = HostContext.Resolve<IHashProvider>();

Does this mean that I can implement and plug-in my own HashProvider? If so how does that work?

UPDATE

I figured out that I can assign keys but only if I put the code into AfterInitCallbacks.Add. It seems to be called AFTER OnAfterInit().

So this code creates the key for the account generated during ServiceStartup:

AfterInitCallbacks.Add(host =>
{

    var authProvider = (ApiKeyAuthProvider)AuthenticateService.GetAuthProvider(ApiKeyAuthProvider.Name);
    var authRepo = (IManageApiKeys)TryResolve<RedisAuthRepository>();
    var userRepo = (IUserAuthRepository)TryResolve<RedisAuthRepository>();
    var user = userRepo.GetUserAuthByUserName("JoeTest");

    var keys = authRepo.GetUserApiKeys(user.Id.ToString());
    if (keys.Count == 0)
    {
        try
        {
            keys = authProvider.GenerateNewApiKeys(user.Id.ToString());
            authRepo.StoreAll(keys);

        }
        catch (Exception e)
        {
            Debug.WriteLine(e);
            throw;
        }
    }
});

At least I can see what is generated on Redis now. However it remains unclear to me how keys can be created automatically.

Community
  • 1
  • 1
ThommyB
  • 1,456
  • 16
  • 34

1 Answers1

1

Firstly you should also only have one PooledRedisClientManager singleton instance to pool all your connections together so you should also change your Redis Client Manager connection to reuse the same instance, e.g:

container.Register<IRedisClientsManager>(c => RedisBusPool);

User Auth Repositories should be registered against the IAuthRepository interface so it can be resolved, so you'll need to change it to:

container.Register<IAuthRepository>(c => new RedisAuthRepository(RedisBusPool));

Which you'd now retrieve with:

var authRepo = (IUserAuthRepository)TryResolve<IAuthRepository>();

As for retrieving the ApiKeyAuthProvider, I've confirmed that this works as expected:

 var authProvider = (ApiKeyAuthProvider)
     AuthenticateService.GetAuthProvider(ApiKeyAuthProvider.Name);

If you've registered ApiKeyAuthProvider in AuthFeature as shown it should be returned. I would double-check that the Null Reference Error isn't due to another issue.

mythz
  • 141,670
  • 29
  • 246
  • 390
  • Thanks, Demis. I just updated my original post. I just make the corrections in your answer. – ThommyB Apr 10 '17 at 15:33
  • @ThommyB I don't know what you mean by **created automatically**? Calling `StoreAll()` is what creates the API Keys. – mythz Apr 10 '17 at 15:41
  • I mean that keys are created with the creation of the user, so I do not have to call `StoreAll()`. The docs say: 'Whilst the API Key Auth Provider will automatically generate API Keys for new users, if .....' – ThommyB Apr 10 '17 at 15:48
  • @ThommyB It gets created when a a [new User is registered](https://github.com/ServiceStack/ServiceStack/blob/f9f7f4b908de22462dcdf0f9a1ce795309cb792a/src/ServiceStack/Auth/ApiKeyAuthProvider.cs#L399) (e.g. using /register Service). But if you're not using the built-in Register Service you can [Raise your own OnRegistered() AuthEvents instead](https://github.com/ServiceStack/ServiceStack/blob/f9f7f4b908de22462dcdf0f9a1ce795309cb792a/src/ServiceStack/Auth/RegisterService.cs#L150-L151). – mythz Apr 10 '17 at 16:03
  • Ah as I expected. Since auto-registration is not an option in this project, I will look into the AuthEvents. I made the changes you mentioned in your answer and now I can confirm it works no matter whether I implement it in `OnAfterInit` or `AfterInitCallbacks`. – ThommyB Apr 10 '17 at 16:18