ServiceStack - IAuthRepository vs IUserAuthRepository - asp.net-core

I’ve to configure my web application to use the ServiceStack built-in ApiKeyAuthProvider. I’ve registered in the container the OrmLiteAuthRepository with the IAuthRepository interface but it throws an exception saying that I’ve not registered the IUserAuthRepository.
Could someone explain me the difference?
Thanks in advance
EDIT:
Sorry, i've made confusion
The error is
System.NotSupportedException: 'ApiKeyAuthProvider requires a registered IAuthRepository'
Our AppHost's Configure method is
public override void Configure(Container container)
{
var dbFactory = new OrmLiteConnectionFactory("connString", SqlServerDialect.Provider);
container.Register<IDbConnectionFactory>(dbFactory);
container.Register<IUserAuthRepository>(_ => new OrmLiteAuthRepository(dbFactory));
container.Resolve<IUserAuthRepository>().InitSchema();
var authProvider = new ApiKeyAuthProvider()
{
RequireSecureConnection = false
};
Plugins.Add(new AuthFeature(
() => new AuthUserSession(),
new IAuthProvider[] {
authProvider
}
));
}
Could you explain me the difference between these two interfaces? we can't figure out (ServiceStack v.6.0.2)

Please refer to the Auth Repository docs for examples of correct usage, e.g:
container.Register<IDbConnectionFactory>(c =>
new OrmLiteConnectionFactory(connectionString, SqlServer2012Dialect.Provider));
container.Register<IAuthRepository>(c =>
new OrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>()));
container.Resolve<IAuthRepository>().InitSchema();
The IAuthRepository is the minimum interface all Auth Repositories have to implement whilst IUserAuthRepository is the extended interface to enable extended functionality to enabled additional features which all ServiceStack built-in Auth Repositories also implement. But you should never need to register or resolve a IUserAuthRepository, i.e. they should only be registered against the primary IAuthRepository interface.
Resolving Auth Repository
If you need to, the Auth Repository can be accessed from base.AuthRepository or base.AuthRepositoryAsync in your Service where you'll be able to use any IUserAuthRepository APIs since they're all available as extension methods on IAuthRepository, e.g. This example Service calls the IUserAuthRepository.GetUserAuth() method:
public class MyServices : Service
{
public object Get(MyRequest request) =>
AuthRepository.GetUserAuth(request.UserId);
}
Whilst here are the recommended APIs to access the Auth Repository outside of your Services:
var authRepo = HostContext.AppHost.GetAuthRepository();
var authRepoAsync = HostContext.AppHost.GetAuthRepositoryAsync();

Related

Is there a way to specify which IAuthProvider to use for authentication on a particular Service class?

I have two services within the same project:
[Authenticate]
public class OnlyDoesBasicAuth : Service
{
}
[Authenticate]
public class OnlyDoesJwtAuth : Service
{
}
//AppHost
public override void Configure(Container container)
{
Plugins.Add(new AuthFeature(
() => new AuthUserSession(),
new IAuthProvider[]
{
new BasicAuthProvider(AppSettings),
new JwtAuthProvider(AppSettings)
}
)
{
HtmlRedirect = null
});
}
The way this is set up, I can get into both services using Basic Authentication or with a valid Bearer token. I'd prefer to have it so that only one service can do one means of authentication. Is there a way to specify with provider to use on authentication?
I'm at a loss on how to approach this. I was thinking that maybe there was a way via global request filter or something to that effect, but maybe that's not the way to go. I could split the project into two projects, which will definitely work, but it's not the way I want to approach it.
I'm just looking for a way for the OnlyDoesBasicAuth to only use the BasicAuthProvider on authentication and OnlyDoesJwtAuth to only use the JwtAuthProvider within the same project. Any and all suggestions will be greatly appreciated.
Thanks in advance.
Typically once you're authenticated using any of the Auth Providers you're considered as an Authenticated User everywhere in ServiceStack.
You can restrict access so that a Service needs to be Authenticated with by specifying the AuthProvider name in the [Authenticate] attribute, e.g:
[Authenticate(BasicAuthProvider.Name)]
public class OnlyDoesBasicAuth : Service
{
}
[Authenticate(JwtAuthProvider.Name)]
public class OnlyDoesJwtAuth : Service
{
}
Alternatively you can validate within your Service that they need to be authenticated with a specific Auth Provider, e.g:
if (SessionAs<AuthUserSession>().AuthProvider != JwtAuthProvider.Name)
throw HttpError.Forbidden("JWT Required");

GetRequiredService from within Configure

I'm trying to access one of my services from within the Configure call within Startup.cs in aspnet core. I'm doing the following however I get the following error "No service for type 'UserService' has been registered." Now I know it is registered because I can use it in a controller so I'm just doing something wrong when it comes to using it here. Please can someone point me in the right direction. I'm happy with taking a different approach to setting up Tus if there's a better way of achieving what I want.
var userService = app.ApplicationServices.GetRequiredService<UserService>();
userService.UpdateProfileImage(file.Id);
The below is where I'm wanting to use
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
... Other stuff here...
app.InitializeSimpleInjector(container, Configuration);
container.Verify();
app.UseTus(httpContext =>
{
var restaurantEndpoint = "/restaurant/images";
var userEndpoint = "/account/images";
var endPoint = "/blank/images";
if (httpContext.Request.Path.StartsWithSegments(new PathString(restaurantEndpoint)))
{
endPoint = restaurantEndpoint;
}
if (httpContext.Request.Path.StartsWithSegments(new PathString(userEndpoint)))
{
endPoint = userEndpoint;
}
return new BranchTusConfiguration
{
Store = new TusDiskStore(#"C:\tusfiles\"),
UrlPath = endPoint,
Events = new Events
{
OnBeforeCreateAsync = ctx =>
{
return Task.CompletedTask;
},
OnCreateCompleteAsync = ctx =>
{
return Task.CompletedTask;
},
OnFileCompleteAsync = async ctx =>
{
var file = await ( (ITusReadableStore)ctx.Store ).GetFileAsync(ctx.FileId, ctx.CancellationToken);
var userService = app.ApplicationServices.GetRequiredService<UserService>();
userService.UpdateProfileImage(file.Id);
}
}
};
});
... More stuff here...
};
My end goal is to move this to an IApplicationBuilder extension to clean up my startup.cs but that shouldn't affect anything if it's working from within startup.cs
Edit: Add to show the registration of the userService. There is a whole lot of other stuff being registered and cross wired in the InitializeSimpleInjector method which I've left out. can add it all if need be..
public static void InitializeSimpleInjector(this IApplicationBuilder app, Container container, IConfigurationRoot configuration)
{
// Add application presentation components:
container.RegisterMvcControllers(app);
container.RegisterMvcViewComponents(app);
container.Register<UserService>(Lifestyle.Scoped);
container.CrossWire<IServiceProvider>(app);
container.Register<IServiceCollection, ServiceCollection>(Lifestyle.Scoped);
}
Please read the Simple Injector integration page for ASP.NET Core very closely, as Simple Injector integrates very differently with ASP.NET Core as Microsoft documented how DI Containers should integrate. The Simple Injector documentation states:
Please note that when integrating Simple Injector in ASP.NET Core, you do not replace ASP.NET’s built-in container, as advised by the Microsoft documentation. The practice with Simple Injector is to use Simple Injector to build up object graphs of your application components and let the built-in container build framework and third-party components
What this means is that, since the built-in container is still in place, resolving components using app.ApplicationServices.GetRequiredService<T>()—while they are registered in Simple Injector—will not work. In that case you are asking the built-in container and it doesn't know about the existence of those registrations.
Instead, you should resolve your type(s) using Simple Injector:
container.GetInstance<UserService>()

How can I use Asp.Net Core 2.0's in-memory TestServer class for Integration Tests when my API requires an Authorization Token?

I am working on an ASP.NET Core 2.0 Web API and I want to do some integration tests using ASP.NET Core's TestServer class. I am using xUnit as my testing framework so I have created a TestServerFixture class that creates the in-memory TestServer instance and then use the TestServer's .CreateClient() to create the HTTPClient instance.
My Web API requires an OAuth2.0 Access Token from my Azure AD. I set this up using this code in my Startup.cs, ConfigureServices method:
// Add Azure AD OAUTH2.0 Authentication Services
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddAzureAdBearer(options => Configuration.Bind("AzureAd", options));
and in my controllers, I have the [Authorize] attribute on the class.
So for my Integration Tests setup, I have a method in my TestServerFixture that obtains a valid token from Azure AD and I add it to my client request header as follows;
Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", await _testServerFixture.GetAccessToken());
When I debug my integration test, I can see that the request does contain a valid access token but I am still getting a 401 Unauthorized from the API when I run my Integration Test.
After doing some digging I found several resources that talk about a similar issue with TestServer, but related to Authentication rather than Authorization, as I am experiencing. Here are links to these resources;
https://medium.com/#zbartl/authentication-and-asp-net-core-integration-testing-using-testserver-15d47b03045a
How do I integration test a ASP 5/Core Web API with [Authorize] Attributes
http://geeklearning.io/how-to-deal-with-identity-when-testing-an-asp-net-core-application/
These all talk about assigning a ClaimsPrincipal to the context.user using custom middleware. Since this is based upon Authentication rather than Authorization, I am not sure if I can do something similar for my Access Token.
I do know that in my API, I can access the HTTPContext.User and pull out the AppId value, which is part of the Access Token so it would seem that Authentication and Authorization both use the Context.User.
So, before I burn time building up my own custom middleware for this purpose, I wanted to see if anyone has already addressed this issue or perhaps are aware of a NuGet that does what I need.
EDIT - SOLUTION
I am showing this in case anyone else runs into this issue.
I ended up building the middleware that Zach Bartlett presented in his blog , but making the following changes.
public class AuthenticatedTestRequestMiddleware
{
#region Class Variables
private const string TestingAccessTokenAuthentication = "TestingAccessTokenAuthentication";
private readonly RequestDelegate _next;
#endregion Class Variables
#region Constructor(s)
public AuthenticatedTestRequestMiddleware(RequestDelegate next)
{
_next = next;
}
#endregion Constructor(s)
public async Task Invoke(HttpContext context)
{
if (context.Request.Headers.Keys.Contains("X-Integration-Testing"))
{
if (context.Request.Headers.Keys.Contains("Authorization"))
{
var token = context.Request.Headers["Authorization"].First();
var claimsIdentity = new ClaimsIdentity(new List<Claim>
{
new Claim(ClaimTypes.Authentication, token)
}, TestingAccessTokenAuthentication);
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
context.User = claimsPrincipal;
}
}
await _next(context);
}
}
There were one interesting "Gotcha".
In Zach's blog he had the code;
public const string TestingHeader = "X-Integration-Testing";
at the top of his middleware and then references the TestingHeader in the test for the key in the header collection like this;
if (context.Request.Headers.Keys.Contains(TestingHeader)
Doing it this way was failing for me until I put the string literal instead of the variable into the .Contains() clause.
Now, my integration test is passing with a 200 OK response. :)
I was able to find a solution following Zach Bartlett's blog post, and making some small changes to make it pertain to the Authentication header. The code is shown as an edit in my original post above.

How do I access Dependency Injection graph in .NET Core 2.0 ConfigureServices()

I am trying to migrate from .NET Core 1.1 to 2.0, and am stuck migrating the JWT Token configuration. I have an interface/class that provides the JWTBearerOptions, and in .NET Core 2.0 I cannot access my DI Graph objects (since in 2.0, JWT is configured in the ConfigureServices() function). I want to keep my Startup.cs file clean of so many lines of code configuring JWT.
Is there any way to delegate the JWTBearerOptions object creation to a provider created through DI? I want something like the below:
public virtual void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication()
.AddJwtBearer(MyAuthicationScheme.JwtAuthName, options =>
{
myInjectedInstance.SetJwtBearOptions(options, Configuration);
})
}
#flodin
I came across this same problem for the AddJwtBearer and there is a cludgey way to get access to the HttpContext by plugging into OnMessageRecieved Events
jwt.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents()
{
OnMessageReceived = context =>
{
// setting the issuer validator delegate here instead of in the jwt.TokenValidationParameters
// allows for accessing the HttpContext items and DI container
context.Options.TokenValidationParameters.IssuerValidator = (issuer, token, parameters) =>
{
// di in callbacks!
var test = context.HttpContext.RequestServices.GetService<ITenant>();
return Task.CompletedTask;
}
Found the answer on microsoft's docs. It is impossible to access DI objects during the ConfigureServices call: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/index?tabs=basicconfiguration#additional-notes

Is is possible to disable authentication providers for specific routes?

We're evaluating service stack v.4.5.6.0 for a Web API and we want clients to be able to authenticate using basic auth or credentials but we do not want them to be able to provide a basic auth header in place of a JWT token or session cookie when using our services. While I realize this is somewhat arbitrary, is there a way to exclude routes from specific providers or force the use of a token/cookie to authenticate once they've logged in?
Auth config from AppHost:
private void ConfigureAuth(Container container)
{
var appSettings = new AppSettings();
this.Plugins.Add(new AuthFeature(() => new CustomAuthUserSession(),
new IAuthProvider[]
{
new CredentialsAuthProvider(),
new BasicAuthProvider(),
new JwtAuthProvider(appSettings)
}) { IncludeAssignRoleServices = false, MaxLoginAttempts = 10} );
var userRepository = new CustomUserAuthRepository(container.TryResolve<IDbConnectionFactory>());
container.Register<IAuthRepository>(userRepository);
}
ServiceStack lets you decide which AuthProviders you want your Services to be authenticated with, but it doesn't let you individually configure which adhoc AuthProviders applies to individual Services. Feel free to add this a feature request.
However if you want to ensure that a Service is only accessed via JWT you can add a check in your Services for FromToken which indicates the Session was populated by a JWT Token, e.g:
[Authenticate]
public class MyServices : Service
{
public object Any(MyRequest request)
{
var session = base.SessionAs<AuthUserSession>();
if (!session.FromToken)
throw HttpError.Unauthorized("Requires JWT Authentication");
//...
}
}
From v4.5.7 that's now available on MyGet you can also use the new session.AuthProvider property which indicates what AuthProvider was used to Authenticate the user, e.g:
public object Any(MyRequest request)
{
var session = base.SessionAs<AuthUserSession>();
if (session.AuthProvider != JwtAuthProvider.Name)
throw HttpError.Unauthorized("Requires JWT Authentication");
//...
}
Refer to the docs for different AuthProvider names for each AuthProvider.