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

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");

Related

Dynamically adding policy claims for Blazor authorization

I am creating an authentication and authorization handler for internal authorization purposes. My intention is to make it easy for my colleagues to implement the solution into their own projects. We are using Azure AD for authentication, and for authorization we are using Azure Groups. In order to do that, I feel like I am stuck on figuring out how to add authorization policies in an efficient way.
Right now I'm adding it through the officially described way in the Program class of my Client project in a Blazor webassembly hosted configuration:
builder.Services.AddAuthorizationCore(options =>
options.AddPolicy("PolicyName", policy =>
{
policy.RequireClaim("ClaimType", "ClaimValue");
})
);
This works fine, but it's not intuitive, as any given project could require several different policies
I have also added a custom Authorization Policy Provider, as described in this documentation from Microsoft:
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/iauthorizationpolicyprovider?view=aspnetcore-6.0
I figured this would be what I was looking for, based on their description for this documentation, especially the first couple of lines in the documentation. But I still can't seem to get it to work as intended, without specifically adding each policy manually.
If need be I can show my custom implementation of the Authorization Policy Provider, but it is pretty much exactly as seen in the Github for the documentation.
Policies are most commonly registered at application startup in the Startup classes ConfigureServices method.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(config =>
{
config.AddPolicy("IsDeveloper", policy => policy.RequireClaim("IsDeveloper","true"));
});
}
the policy IsDeveloper requires that a user have the claim IsDeveloper with a value of true.
Roles you can apply policies via the Authorize attribute.
[Route("api/[controller]")]
[ApiController]
public class SystemController
{
[Authorize(Policy = “IsDeveloper”)]
public IActionResult LoadDebugInfo()
{
// ...
}
}
Blazors directives and components also work with policies.
#page "/debug"
#attribute [Authorize(Policy = "IsDeveloper")]
< AuthorizeView Policy="IsDeveloper">
< p>You can only see this if you satisfy the IsDeveloper policy.< /p>
< /AuthorizeView>
Easier Management
With role-based auth, if we had a couple of roles which were allowed access to protected resources - let’s say admin and moderator. We would need to go to every area they were permitted access and add an Authorize attribute.
[Authorize(Roles = "admin,moderator")]
This doesn’t seem too bad initially, but what if a new requirement comes in and a third role, superuser, needs the same access? We now need to go round every area and update all of the roles. With policy-based auth we can avoid this.
We can define a policy in a single place and then apply it once to all the resources which require it. Then when extra roles need to be added, we can just update the policy from the central point without the need to update the individual resources.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(config =>
{
config.AddPolicy("IsAdmin", policy => policy.RequireRole("admin", "moderator", "superuser"));
});
}
[Authorize(Policy = "IsAdmin")]
Creating shared policies
We need to install the Microsoft.AspNetCore.Authorization package from NuGet in order to do this.
After that create a new class called Policies with the following code.
public static class Policies
{
public const string IsAdmin = "IsAdmin";
public const string IsUser = "IsUser";
public static AuthorizationPolicy IsAdminPolicy()
{
return new AuthorizationPolicyBuilder().RequireAuthenticatedUser()
.RequireRole("Admin")
.Build();
}
public static AuthorizationPolicy IsUserPolicy()
{
return new AuthorizationPolicyBuilder().RequireAuthenticatedUser()
.RequireRole("User")
.Build();
}
}
Here we’re using the AuthorizationPolicyBuilder to define each policy, both require the user to be authenticated then be in either the Admin role or User role, depending on the policy.
Configuring the server
Rregistering the policies in ConfigureServices in the Startup class. Add the following code under the existing call to AddAuthentication.
services.AddAuthorization(config =>
{
config.AddPolicy(Policies.IsAdmin, Policies.IsAdminPolicy());
config.AddPolicy(Policies.IsUser, Policies.IsUserPolicy());
});
registering each policy and using the constants we defined in the Policies class to declare their names, which saves using magic strings.
If we move over to the SampleDataController we can update the Authorize attribute to use the new IsAdmin policy instead of the old role.
[Authorize(Policy = Policies.IsAdmin)]
[Route("api/[controller]")]
public class SampleDataController : Controller
Again, we can use our name constant to avoid the magic strings.
Configuring the client
Our server is now using the new policies we defined, all that’s left to do is to swap over our Blazor client to use them as well.
As with the server we’ll start by registering the policies in ConfigureServices in the Startup class. We already have a call to AddAuthorizationCore so we just need to update it.
services.AddAuthorizationCore(config =>
{
config.AddPolicy(Policies.IsAdmin, Policies.IsAdminPolicy());
config.AddPolicy(Policies.IsUser, Policies.IsUserPolicy());
});
In Index.razor, update the AuthorizeView component to use policies - still avoiding the magic strings.
< AuthorizeView Policy="#Policies.IsUser">
< p>You can only see this if you satisfy the IsUser policy.< /p>
< /AuthorizeView>
< AuthorizeView Policy="#Policies.IsAdmin">
< p>You can only see this if you satisfy the IsAdmin policy.< /p>
< /AuthorizeView>
Finally, update FetchData.razors Authorize attribute.
#attribute [Authorize(Policy = Policies.IsAdmin)]
Refer here

Is there a way to get a non-null Principal/Authentication using Spring Security with anonymous access?

I'm using Spring Security, and want to be able to run with a "local" profile with security off. I can do this by using a profile-controlled WebSecurityConfigurerAdapter which allows anonymous access, but the problem is that any Principal or Authentication arguments to controller methods are null.
The way I've solved this before is by passing the arguments through an adapter, which has a non-local pass-through implementation and a "local"-profile implementation that substitutes a configured value:
#GetMapping()
public ResponseEntity<List<Thing>> getThings(Principal principal) {
Principal myPrincipal = securityAdapter.principal(principal);
...
}
But this feels really clunky. Is there some way to configure Spring Security to make the substitution so that I can keep the code clean?
Unfortunately it is a SpringSecurity feature: the AnonymousAuthenticationToken is not injected in controller methods. I could find some references for that in this issue.
A possible workaround (from above issue) is to inject a custom trust resolver that pretends that the AnonymousAuthenticationToken is not anonymous...
#Bean
public AuthenticationTrustResolver trustResolver() {
return new AuthenticationTrustResolver() {
#Override
public boolean isRememberMe(final Authentication authentication) {
return false;
}
#Override
public boolean isAnonymous(final Authentication authentication) {
return false;
}
};
}
Rather ugly, but as it is intended to run in a profile where anonymous authentication is expected to be valid, it could be enough.

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.

Claims based authentication, with active directory, without ADFS

I have a client asking for an integrated authentication based solution utilizing a custom role/membership schema. My original plan was to use claims based authentication mechanism with integrated authentication. However, my initial research is not turning up a whole lot of useful information.
To the point, I have an ASP.NET (not core nor owin) WebAPI application, which has api actions used by angular SPA based (asp.net) web application. I am attempting to authorize the api calls using integrated authentication. My initial effort was focused around a custom AuthorizationAttribute and ClaimsAuthenticationManager implementation. However as I got deeper into that I started running into issues with the custom ClaimsAuthenticationManager, at this point I'm not sure that is the proper route to take.
So my question for you all is, can you at least give me some ideas of what it would take to make this happen? I don't need help with secific bits the code, just need to figure out the appropriate "stack" so to speak.
The only real requirement is WebAPI calls can be authorized, with a custom attribute passing a name of a claim to authorize on, but the claim is not in AD even though it is using windows authentication, the claims themselves would come from a database.
Thank you all in advance!
Look at https://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-api.
Your scenario isn't much different:
you're using AD for authentication
you're using your db for authorization
Simply put this can be addressed by configuring web-api to use windows authentication.
<system.web>
<authentication mode="Windows" />
</system.web>
And add your own IAuthorizationFilter to Web API pipeline, that will check current principal (should be set), and then override this principal with your own (i.e. query db - get claims, and override it with your custom claims principal by setting HttpContext.Current.User and Thread.CurrentPrincipal).
For how to add filter to WebAPI pipe line check out How to add global ASP.Net Web Api Filters?
public class CustomAuthenticationFilter : IAuthenticationFilter {
public bool AllowMultiple { get { return true; } }
public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken) {
var windowsPrincipal = context.Principal as WindowsPrincipal;
if (windowsPrincipal != null) {
var name = windowsPrincipal.Identity.Name;
// TODO: fetch claims from db (i guess based on name)
var identity = new ClaimsIdentity(windowsPrincipal.Identity);
identity.AddClaim(new Claim("db-crazy-claim", "db-value"));
var claimsPrincipal = new ClaimsPrincipal(identity);
// here is the punchline - we're replacing original windows principal
// with our own claims principal
context.Principal = claimsPrincipal;
}
return Task.FromResult(0);
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken) {
return Task.FromResult(0);
}
}
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
config.Filters.Add(new CustomAuthenticationFilter());
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute( ... );
}
}
Also there is no need for custom authorization attribute - use default one - its understood by everyone, and makes your code more readable.

ASP Core Add Custom Claim to Auth Token

I'm using openiddict and ASP Identity, and I am trying add a "GroupId" as a claim to the auth token that is returned when logging in (using the /connect/token endpoint - see example I followed below). The GroupId is a property in my AplicationUser class.
I have tried using an IClaimsTransformer but that seems clunky, I can't easily get to the UserManager from the ClaimsTransformationContext.
How would I go about either getting the UserManager through DI in my IClaimsTransformer or just adding the GroupId to the token that is generated at the connect/token endpoint?
I followed this example for setting up my site. This is what I would like to do:
var groupGuid = User.Claims.FirstOrDefault(c => c.Type == "GroupGuid");
There is a couple of ways to achieve it:
First, override CreateUserPrincipalAsync method in your custom SignInManager:
public override async Task<ClaimsPrincipal> CreateUserPrincipalAsync(ApplicationAdmin user)
{
var principal = await base.CreateUserPrincipalAsync(user);
// use this.UserManager if needed
var identity = (ClaimsIdentity)principal.Identity;
identity.AddClaim(new Claim("MyClaimType", "MyClaimValue"));
return principal;
}
The second way is to override CreateAsync method of your custom UserClaimsPrincipalFactory:
public override async Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
{
var principal = await base.CreateAsync(user);
var identity = (ClaimsIdentity)principal.Identity;
identity.AddClaim(new Claim("MyClaimType", "MyClaimValue"));
return principal;
}
which is, basically, the same, because base.CreateUserPrincipalAsync method in SignInManager calls this.UserClaimsPrincipalFactory() inside.
Don't forget to add your custom implementations into services:
either
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSignInManager<CustomSignInManager>();
}
or
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, CustomClaimsPrincipalFactory>();
}
ASP Core Add Custom Claim to Auth Token
You can't change a token after it is signed by IdP, so you can't add claim to token.
I have tried using an IClaimsTransformer but that seems clunky, I
can't easily get to the UserManager from the
ClaimsTransformationContext.
I guess your problem is related this github issue. In summary(as far as i understand) if ClaimsTransformer class is registered as singletion and one of the its dependency is scoped or transient, it causes captive dependency. In this case you should use Service Locator pattern to avoid from captive dependency. Your code may be something like this(from #PinpointTownes' comment):
public async Task<ClaimsPrincipal> TransformAsync(ClaimsTransformationContext context)
{
var userManager= context.Context.RequestServices.GetRequiredService<UserManager>();
//
}
-- My thoughts about your case --
You have basically two options to achieve your goal:
Add claim to token when token is generated by IdP:
You don't need this method most cases, but if you want to use it:
You should have control over IdP, because this option is possible on the IdP(as far as i understand your IdP and Resource Server is same, so you have control over IdP, but it might not be possible always) .
You should take care of inconsistency when using this option, because the claim is stored in the token and doesn't get each request. So the real value of claim might be different from claim in the token.(I don't prefer it for roles, permissions, groups etc. because these claims can be change anytime).
p.s: i don't know if it is possible to add claims to token with Openiddict.
Claims Transformation
Actually i used HttpContext.Items to store additional claims before i discovered this method and it worked well for me. But i think better way is to use Claims Transformation and it fits into your case.