How should the restriction be done when the subscription period expires? - c#

In a Rest Api SaaS project developed with .Net Core 3.1.
When the user's subscription expires (needs to pay), what kind of a method would be better to follow.
There are 2 methods that I think of but I think there will be some problems in both of them.
Method 1) Checking the subscription status during JWT generate and not generating JWT if the subscription period has expired:
If I use this method;
Advantage: Since a token is not given to a user whose subscription expires,
they will not be able to access other endpoints.
I think this will work extremely safe without doing any other coding work.
Disadvantage: When I need to redirect the user to the payment page,
I will have to do a special work for the payment endpoints since there are no tokens.(Example: Password Reset Methods)
I will get it with query string, I think I can create a special token for this method.
But I think there might be a security bug because I couldn't protect this process with my standard authorization method?
Method 2) Even if the subscription expires, jwt will be generated, but membership will be restricted:
If I use this method;
Advantage: I can use my standard authorization method without any problems
when I need to direct the user to the payment endpoints or to another endpoints.
I will use with jwt and security bugs will be considerably reduced.
Disadvantage: I need to determine endpoints that cannot be accessed on the application for user whose subscription period expired
and I will need to code a working service in middleware that will make them inaccessible. (Like to permission methods)
This will both do extra coding work and each endpoint will require extra work.
These are my thoughts....
Or other solutions...
How should we restrict a user whose subscription expires and how should we act?
Thank you very much for your information sharing.

I solved the question I asked above using Method 2.
I wanted to explain how I did it, as I thought it might help those who investigate this question in the future.
I said in method 2, jwt has generated but membership restricted.
First of all, when generating tokens, I set claims whether they have a subscription or not.
....
new Claim(JwtClaimIdentifier.HasSubscription, hasSubscription)
I do not explain here in detail. Standard claims.
Subscription Control
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public sealed class SubscriptionRequiredAttribute : TypeFilterAttribute
{
public SubscriptionRequiredAttribute()
: base(typeof(SubscriptionFilter)) { }
}
--
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public sealed class AllowWithoutSubscriptionAttribute : Attribute
{
public AllowWithoutSubscriptionAttribute() { }
}
--
public class SubscriptionFilter : IAuthorizationFilter
{
private bool AllowWithoutSubscription(AuthorizationFilterContext context)
{
var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
bool allowWithoutSubscriptionForMethod = controllerActionDescriptor.MethodInfo.CustomAttributes.Any(x => x.AttributeType == typeof(AllowWithoutSubscriptionAttribute));
if (allowWithoutSubscriptionForMethod)
return true;
bool allowWithoutSubscriptionForController = controllerActionDescriptor.ControllerTypeInfo.CustomAttributes.Any(x => x.AttributeType == typeof(AllowWithoutSubscriptionAttribute));
if (allowWithoutSubscriptionForController)
return true;
return false;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
if (AllowWithoutSubscription(context))
return;
var hasSubscription = context.HttpContext.User.Claims.First(x => x.Type == JwtClaimIdentifier.HasSubscription).Value.ToLower() == "true";
if (!hasSubscription)
context.Result = new BadRequestObjectResult(**ErrorCode**);
}
}
I added, an attribute that override subscription control.
For example; To use it in a controller or method that I need to override when checking subscriptions on base.
Use Controller
[SubscriptionRequired]
public class FooController
{
public async Task<IActionResult> FooMethodOne(){...}
public async Task<IActionResult> FooMethodTwo(){...}
[AllowWithoutSubscription]
public async Task<IActionResult> FooMethodThree(){...}
}
While FooMethodOne and FooMethodTwo above require subscription, FooMethodThree will work without subscription.
Likewise, all controls are called "AllowWithoutSubscription".
It can also be called "SubscriptionRequired" in methods.
Hopefully it benefits your business...

Related

How to pass/verify Open ID token between .net core web app and web api?

We have the following architecture:
.NET core 3.1 web application using razor pages, jQuery, etc as the UI (not an angular application)
.NET core 3.1 web api application serving as our api layer
Okta as our identity provider
I have implemented the Okta widget and middleware in the web application. Users can login, and after that happens I’m able to get a ClaimsPrincipal, access all of their scopes, and get to any custom profile data I’ve stored via open id. I'm able to secure views through the [Authorize] decoration. All that is working perfectly.
What I need to do now is implement the security checks on the API side. I’ve spent hours and hours looking at examples and have found many, but I'm either missing something obvious or what I'm doing is unique (and I can't imagine that what I'm doing is that unique). Basically what I need to do is:
Have the web app pass the auth and id tokens to the api
Have the api be able to verify the token and then decipher user information from the id token
This would then allow me to implement the necessary security logic on the API side. Let’s say its the API that returns customer orders - well I need to make sure that the user calling it is either an administrator or is the actual customer (so I don’t return customer data to someone who shouldn’t see it). I have all the role stuff figured out, I just can’t, for the life of me, figure out how to determine who someone is via the token?
Passing the tokens is pretty straightforward, but how would I get the token out of the ClaimsPrincipal object? Or do I need to call the Okta API after the user logs in to specifically get the access and id tokens?
Then of course I'll have to figure out how to get the API side to properly validate and parse the token that is sent.
If anyone could help me get started with this or point me in the right direction for an example, I would be very appreciative. At this point I have read every article on Owin, OpenID, Okta, authorization in .net core I could find.
Thanks to Cameron Tinker's suggestion I was able to get this working. There were a few things that tripped me up, so I'll share them here in case anyone experiences the same.
If you're using Okta, you can do all of this through the Okta middleware package. You can do it just using the c# OpenID library, but the Okta.AspNetCore library will help things along.
First you register the middleware in the web app. Okta has lots of examples of this on their site and its pretty straightforward.
Within your web app you can use this to grab the token (after a user has authenticated of course)
await context.HttpContext?.GetTokenAsync("id_token")
Send that along in your API calls as part of the header using via the standard mechanism:
"Authorization" : "Bearer [token]"
On the Web API side, you use the same Okta.AspNetCore middleware package and can then decorate your controllers with [Authorize] to enforce auth on them. Here is where I got tripped up. If you are not using the default auth server in Okta and have setup a custom one for your application, you need to specific it and the audience in your config:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = OktaDefaults.ApiAuthenticationScheme;
options.DefaultChallengeScheme = OktaDefaults.ApiAuthenticationScheme;
options.DefaultSignInScheme = OktaDefaults.ApiAuthenticationScheme;
})
.AddOktaWebApi(new OktaWebApiOptions()
{
OktaDomain = oktaDomain,
AuthorizationServerId = authServerId,
Audience = clientId
});
services.AddAuthorization();
I had complete forgotten about the audience part - and with the way token validation works, that part is required.
From there, the middleware takes care of populating an ClaimsPrincipal for you, so you can access user information via the ClaimsPrincipal (HttpContext.User). I ended up creating a "CurrentUserService" and pulled it out into its own library so that I can consolidate all my auth handlers there; thereby allowing my web app and web api code to check permissions and retrieve information about the current user in the same way. That code is here if you're interested:
public interface ICurrentUserService
{
public ClaimsPrincipal GetCurrentUser();
public string GetCurrentUserDisplayName();
public string GetCurrentUserFullName();
public string GetCurrentUserId();
public DateTime? GetCurrentUserDob();
public string GetCurrentUserGender();
public AddressFromClaimsDTO GetCurentUserAddress();
public bool IsAuthenticated();
}
public class CurrentUserService : ICurrentUserService
{
private const string FULL_ADDRESS_CLAIM_TYPE = "address";
private readonly IHttpContextAccessor _context;
public CurrentUserService(IHttpContextAccessor context)
{
_context = context;
}
/// <summary>
/// Gets whether or not the current user context is authenticated.
/// </summary>
/// <returns></returns>
public bool IsAuthenticated()
{
return GetCurrentUser().Identity.IsAuthenticated;
}
/// <summary>
/// Gets the current user's address.
/// TODO: tie this into our address data model... but if addresses live in Okta what does that mean?
/// </summary>
/// <returns></returns>
public AddressFromClaimsDTO GetCurentUserAddress()
{
var addressClaim = GetClaim(FULL_ADDRESS_CLAIM_TYPE);
if (addressClaim != null)
{
//var parseValue = addressClaim.Value.ToString().Replace("{address:", "{\"address\":");
var address = JsonSerializer.Deserialize<AddressFromClaimsDTO>(addressClaim.Value.ToString());
return address;
}
else
{
return new AddressFromClaimsDTO();
}
}
public ClaimsPrincipal GetCurrentUser()
{
return _context.HttpContext.User;
}
public string GetCurrentUserDisplayName()
{
return GetCurrentUser().Identity.Name;
}
public string GetCurrentUserFullName()
{
throw new NotImplementedException();
}
public string GetCurrentUserId()
{
throw new NotImplementedException();
}
public DateTime? GetCurrentUserDob()
{
var claim = GetClaim("birthdate");
if (claim != null && !string.IsNullOrEmpty(claim.Value))
{
return DateTime.Parse(claim.Value);
}
else
{
return null;
}
}
public string GetCurrentUserGender()
{
return GetClaim("gender")?.Value.ToString();
}
public Claim GetClaim(string claimType)
{
return _context.HttpContext.User.FindFirst(x => x.Type == claimType);
}
}
Your ID Provider, Okta in this case, will issue an OpenID Connect authorization bearer token that you will need to pass along to any application that you want to protect.
On the Web Api side of your application, you will need to register your middleware for handling processing of Okta's OpenID Connect tokens. Then you can decorate your controllers/actions with [Authorize] and you can check an identity's claims.

JWT "self" authentication in Net Core 2.2 MVC WebApi

I want to improve my API's security with some sort of "self" policy to validate the call to some user actions (like DELETE user) is made by the same user the token was issued to. Is there a way to do this in a similar way to the policy based authorization?
I have a .Net Core 2.2 with MVC WebAPI running on Kestrel. I have users, roles and user-roles and I have token-based authentication with roles enabled. I can issue tokens and validate then with the "Authorize" attribute in the controllers. However, I've been looking for a way to validate that some actions to users are made only by the users itself, a "self" authentication policy to validate that, for example, user 3 is trying to delete user 3 and only user 3. I've dug up to the claims and everything and I know I can make a simple service passing the claims and the validating it but I wanted to do it in a smoother way similar to the policy-based or role-based authentication. I don't know if I can make it with some sort of middleware or something but it would be great to be able to make it as clean as possible.
[Edit]
The main purpose is to avoid users to delete resources created by other users and make them be able only to delete resources created by themselves.
[Edit2 - Solution]
Thanks to Paul Lorica's Answer I can now describe how I did it.
The first thing is to create a Requirement and a Handler similar to the examples provided by Microsoft in the docs. What we do is to add a Claim to the token generation method/service we have and add the ID as NameIdentifier. After that, we inject in the IHttpContextAccessor in the handler. And then we can validate if the ID in the request is the same than the Id in the Claim. So it was very easy.
I'm adding examples of logic to make it work.
PS: Inject IHttpContextAccessor as a singleton in the startup clas or it won't work.
Handler:
public class SelfUserHandler: AuthorizationHandler<SelfUserRequirement>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public SelfUserHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
SelfUserRequirement requirement)
{
if (!context.User.HasClaim(c => c.Type == ClaimTypes.NameIdentifier))
{
return Task.CompletedTask;
}
var nameIdentifier = context.User.FindFirst(c => c.Type == ClaimTypes.NameIdentifier).Value;
if (_httpContextAccessor.HttpContext.Request.Path.ToString().ToUpper().Contains(nameIdentifier.ToUpper()))
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
return Task.CompletedTask;
}
}
Requirement
public class SelfUserRequirement : IAuthorizationRequirement
{
public SelfUserRequirement() { }
}
Additional info:
Nate Barbettini Answer here
Joe Audette Answer here
First off, when your code validates against the policy, the policy has no understanding, and does not need to know, what you are doing.
I suppose you can retrieve the context via URL. So say if its a DELETE user/3
then you can create a policy that would check the user's claims that it has an ID == 3.
See the docs here on creating policies and accessing the httpContext
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.2
Its a bit of a naive check, I would rather just place that logic within the method of the controller.

Hierarchical policies / requirements in ASP.NET Core Identity

I'm just getting started with ASP.NET Core Identity and have the following requirements defined:
public sealed class IsCustomerUserRequirement : IAuthorizationRequirement
public sealed class IsSuperUserRequirement : IAuthorizationRequirement
With the following basic handlers:
public class IsCustomerUserHandler : AuthorizationHandler<IsCustomerUserRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsCustomerUserRequirement requirement)
{
if (context.User.HasClaim(_ => _.Type == "customer"))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
public class IsSuperUserHandler : AuthorizationHandler<IsSuperUserRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsSuperUserRequirement requirement)
{
if (context.User.IsInRole("super_user"))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
I can then put these inside basic policies:
services
.AddAuthorization(options =>
{
options.AddPolicy("MustBeSuperUser", policy => policy.Requirements.Add(new IsSuperUserRequirement()));
options.AddPolicy("CustomersOnly", policy => policy.Requirements.Add(new IsCustomerUserRequirement()));
});
And apply it using [Authorize("CustomersOnly")], which works fine.
My requirement is to be able to allow super users, claim principals with the super_user role but without the customer claim, to also access Customers Only areas.
I have currently implemented this by changing the handler to manually check:
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsCustomerUserRequirement requirement)
{
if (context.User.HasClaim(_ => _.Type == Claims.Customer) ||
context.User.IsInRole(Roles.SuperUser))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
My issue is this feels like I'm missing the point. Is there a better way to define this so I don't have to repeat the super user check in each handler in future?
The bigger picture in all this is I use IdentityServer4 (ASP.NET Identity-backed) to Authenticate, and then intend to use some JWT-based claims (one claim, two roles) to further identify the user Authorisation falls into an application-specific roles / permissions structure and some custom middleware that has nothing to do with Identity Server. What, if any, best practices are there around this topic?
“this feels like I'm missing the point” – Yes, in a way you are missing the point. You are doing role based authorization: A user can be a customer or a super user.
But instead, the new model is claims based authorization where the user has a claim about something, and you are using that to authorize them. So ideally, the super user would get the same claim the customer gets, and is allowed access to the resource that way. Such a claim also wouldn’t be called customer then, but be rather something that is a property of the user.
You can still use a role-based authorization model with claims but you should probably avoid mixing them. As you noticed yourself, this gets a bit weird eventually.
That being said, there are multiple ways to succeed a policy using different requirements. If you were using roles only (instead of that customer claim), you could simply use the built-in way:
options.AddPolicy("MustBeSuperUser", policy => policy.RequireRole("super_user"));
options.AddPolicy("CustomersOnly", policy => policy.RequireRole("customer", "super_user"));
That way, the CustomersOnly policy would be fulfilled by both customer and super_user roles.
Since you aren’t using a role for your customers, you will have to follow your requirements implementation here. The way authorization requirements work though is that you can have multiple handlers for the same requirement type and only one of them needs to succeed (as long as none fails) for the requirement to be successful.
So you could have your IsSuperUserHandler handle multiple requirements. YOu can follow the AuthorizationHandler<T> implementation to make this work:
public class IsSuperUserHandler : IAuthorizationHandler
{
public virtual async Task HandleAsync(AuthorizationHandlerContext context)
{
foreach (var req in context.Requirements)
{
if (req is IsSuperUserRequirement || req is IsCustomerUserRequirement)
{
if (context.User.IsInRole("super_user"))
context.Succeed(req);
}
}
}
}
So your IsSuperUserHandler is now an authorization handler for both the IsSuperUserRequirement and the IsCustomerUserRequirement. So the CustomersOnly policy that requires the IsCustomerUserRequirement will also be fulfilled for super users.

Pass parameters to a Requirement/Policy in ASP.NET MVC 6

I was trying to make a custom authorization attribute in ASP.NET vNext, until I found this excelent answer from #blowdart in this post:
https://stackoverflow.com/a/31465227/1756978
indicating that Authorization requirements is now the way to go. The answer is very clarifying but doesn't indicates how to pass a parameter to this requirements / policies.
What I'm trying to do is porting a MVC 5 custom authorization attribute which has this signature:
[Autorizacion(Requires = enumPermission.DeleteCustomer)]
since I use a very customised set of permissions mirrored in the backend/frontend as enums/strings.
As this features are still not documented I feel a little lost... Could anybody give guidance about?
Thanks in advance
I happen to comes up with a workround that can satisfy my requirement, hope it will help your too.
In my case, I need to pass IHttpContextAccessor and EFCore's AppDbContext to my Requirement class.
in my Startup.cs, I write something like this:
services.AddAuthorization(options =>
{
options.AddPolicy("ThePolicy", policy => policy.Requirements.Add( new ThePolicyRequirement() ));
});
services.AddScoped<IAuthorizationHandler, ThePolicyAuthorizationHandler>();
the ThePolicyAuthorizationHandler class:
public class ThePolicyAuthorizationHandler : AuthorizationHandler<ThePolicyRequirement>
{
readonly AppDbContext _appContext;
readonly IHttpContextAccessor _contextAccessor;
public ThePolicyAuthorizationHandler(AppDbContext c, IHttpContextAccessor ca)
{
_appContext = c;
_contextAccessor = ca;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ThePolicyRequirement requirement)
{
var result = await requirement.isPass(_appContext, _contextAccessor, context);
if (result)
context.Succeed(requirement);
else
context.Fail(requirement);
}
}
and ThePolicyRequirement class:
public class ThePolicyRequirement : IAuthorizationRequirement
{
AppDbContext _context;
IHttpContextAccessor _contextAccessor;
AuthorizationHandlerContext _authHandlerContext;
public async Task<bool> isPass(AppDbContext context, IHttpContextAccessor contextAccessor, AuthorizationHandlerContext authorizationHandlerContext)
{
_context = context;
_contextAccessor = contextAccessor;
_authHandlerContext = authorizationHandlerContext;
//logic here
return result;
}
}
The key idea is using ThePolicyAuthorizationHandler to obtain as much as possible all needed objects, and pass it to ThePolicyRequirementto do the logic of the authorization mechanism.
Indeed, #blowdart’s post is very insightful and from my understanding, the key thing to understand is the following:
Authorization act upon Identities. Identities are created by
authentication.
So it seems that identities are created by the authentication process.
Then (if you wish) you can make the authorization process kick in. This means creating a custom authorization requirements to which this requirement will be looking at those identities and act upon them.
In plain English, this is what I believe is happening:
As mentioned in blowdart’s post, we should have some sort of
authentication middleware that happens to do the actual
authentication. Once successfully authenticated, you take whatever
information you want from that now-authenticated user and create an
authenticated ClaimsPrincipal.
For example, we could store into that ClaimsPrincipal, the sets of
permission the user has.
Then, when you create your authorization requirement you look at the
ClaimsPrincipal, extract the sets of permissions from the
ClaimsPrincipal and take appropriate action based on whatever
business rules you want.
Assuming you can’t store the sets of permission into the
ClaimsPrincipal for some reason, one could easily store the UserId
and from within the requirement, read that UserId from the
ClaimsPrincipal, invoke the database and get the sets of permissions
and then act upon them.
Conclusion:
So in short, I don’t think you pass stuff to the requirement(s), I think you obtain them from within a ClaimsPrincipal.
In your example, you could create a requirement that reads the ClaimsPrincipal and compare whatever value with your Enum and act upon that.
Let us know what you’ve managed to do and if it works.
And if my understanding of this is wrong, then by all means, feel free to correct me since all of this is new stuff :-)

Best practise for optional injecting of current user

In our ASP.NET MVC project we are using Ninject to resolve our dependencies needed by the controllers.
One of these dependencies is the current user HttpContext.Current.User.Identity. If the user is authenticated we would like to instantiate a user object and several services which relies on it. But we would like to do this not manually but let ninject inject these instances to the controller.
So we get into trouble now, since a url can be located without being authenticated of course. Then ninject tries to resolve the instances before asp.net can redirect to the login page.
I can think of the solution, that we configure ninject do just inject when user is authenticated:
kernel.Bind<User>().ToMethod(GetUser).When(context => HttpContext.Current.User.Identity.IsAuthenticated).InRequestScope();
The problem here is that even if the user is not authenticated ninject instantiates a default object, so my services crashes or needs to check the instance anyhow.
Null checks would me much more acceptable but I wouldn't like to activate AllowNullInjection setting of Ninject.
So my question is whats the best practise for doing such conditional things?
Are there Ninject features I could use in these cases or shouldn't I inject these dependencies anyway?
I assume you are talking about a situation where a non-authenticated user could try to navigate to a page that normally requires authentication, but without first going through the login process. Ninject would then be unable to inject the current user object into the controller because it's not yet known and will throw an exception.
I can see 2 options:
The first option is instead of injecting the current user, create a factory or provider that retrieves the current user details and inject this instead. The controller can then call the provider to get the current user and if the user is unavailable you can redirect to the login page.
public OrdersController(IUserProvider userProvider)
{
this.userProvider = userProvider
}
public void DoSomething()
{
var user = this.userProvider.GetCurrentUser();
if (user == null)
RedirectToLogin();
// continue doing something
}
public class UserProvider : IUserProvider
{
public User GetCurrentUser() { ... }
}
The problem with this option is that you'll need to do this potentially in many controllers (it's a "cross cutting concern") and you don't want to have to repeat the code that does the redirect over and over. Instead, a second option would be to use the Decorator design pattern to create an interceptor that checks for logged in users before forwarding on to the real controller.
The way I've done something similar in the past is using the Ninject Interception Extension to create an attribute that marks which controllers require authentication, like this (bit psuedo-codey):
public class AuthenticationInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
bool authenticated = // ... get the current user ...
if (authenticated)
invocation.Proceed();
else
RedirectToLoginPage(); // however you want to do this
}
}
public class RequiresAuthenticationAttribute : InterceptAttribute
{
public override IInterceptor CreateInterceptor(IProxyRequest request)
{
return request.Context.Kernel.Get<AuthenticationInterceptor>();
}
}
[RequiresAuthentication]
public class OrdersController : IOrdersController
{
// assume you've already been authenticated
}
The interceptor will automatically be created whenever a class that's decorated with RequiresAuthentication is created and the current user credentials will be checked. If they are invalid, the request will be forwarded to the login page, otherwise it will continue as normal. This one interceptor can then be written and tested once whilst being used in many places without duplicating code.
Just as a simple auth and non auth answer that some may find useful.
kernel.Bind<ICustomUser>()
.To<User>()
.When(ctx => HttpContext.Current.User.Identity.IsAuthenticated)
.InRequestScope();
kernel.Bind<ICustomUser>()
.To<Guest>()
.When(ctx => !HttpContext.Current.User.Identity.IsAuthenticated)
.InRequestScope();
Otherwise anything more complex Adam Rodgers awnser is better :)

Categories