Search code examples
servicestack

Authentication failing in ServiceStack integration test


I have scaffolded a ServiceStack project with authentication, and have applied the [Authenticate] attribute to one of my services. The authentication process works fine when I launch the application and log in and execute the service.

However, I am struggling with getting this to work in an Integration Test. It keeps failing with a Unauthorized response.

Here is my integration test:

public class IntegrationTest
{
    const string BaseUri = "http://localhost:2000/";
    private readonly ServiceStackHost appHost;

    class AppHost : AppSelfHostBase
    {
        public AppHost() : base(nameof(IntegrationTest),
            typeof(MyServices).Assembly) { 
        }

        public override void Configure(Container container)
        {
            container.Register<IDbConnectionFactory>(c =>
                new OrmLiteConnectionFactory("Server=*redacted*;User Id=*redacted*;Password=*redacted*;..."));
            container.Register<IAuthRepository>(c =>
                new OrmLiteAuthRepository<Framework.Types.AppUser, UserAuthDetails>(c.Resolve<IDbConnectionFactory>())
                {
                    UseDistinctRoleTables = true
                });

            Plugins.Add(new AuthFeature(() => new Framework.Types.TenantUserSession(),
                    new IAuthProvider[] {
                        new CredentialsAuthProvider(AppSettings),     /* Sign In with Username / Password credentials */
                    }));


            var authRepo = container.Resolve<IAuthRepository>();
            authRepo.InitSchema();
            CreateUser(authRepo, "[email protected]", "Admin User", "p@55wOrd", roles:new[]{ RoleNames.Admin });
        }
    }

    static void CreateUser(IAuthRepository authRepo, string email, string name, string password, string[] roles)
    {
        if (authRepo.GetUserAuthByUserName(email) == null)
        {
            var newAdmin = new Framework.Types.AppUser { Email = email, DisplayName = name };
            var user = authRepo.CreateUserAuth(newAdmin, password);
            authRepo.AssignRoles(user, roles);
        }
    }
    
    public IntegrationTest()
    {
        appHost = new AppHost()
            .Init()
            .Start(BaseUri);
    }

    [OneTimeTearDown]
    public void OneTimeTearDown() => appHost.Dispose();

    //Create the client with username and password of created user.
    public IServiceClient CreateClient() => new JsonServiceClient(BaseUri) { UserName = "[email protected]", Password = "p@55wOrd" };

    [Test]
    public async Task Can_execute_authenticated_operation()
    {
        var client = CreateClient();

        var response = await client.PostAsync(new Framework.ServiceModel.MetaOperationExecute()); //This line fails with 401 Unauthorized
        Assert.That(response.token, Is.Not.Null);
    }
}

How do I get my test to be authenticated?


Solution

  • The UserName/Password for service clients is only for HTTP Basic Authentication using the BasicAuthProvider.

    As you're using CredentialsAuthProvider you'll need to authenticate before calling protected services:

    var client = new JsonServiceClient(BaseUri);
    
    // Establishes authenticated client:
    await client.PostAsync(new Authenticate {
        provider = "credentials",
        UserName = "[email protected]", 
        Password = "p@55wOrd",
    });
    
    // Call protected service:
    var response = await client.PostAsync(new MetaOperationExecute());