2

I'm developing an ASP.Net application with OWIN and currently I have a problem with claims. I have two levels of application: basic and advanced. Some features are available for advanced users only. So I check claims, and if user doesn't have claim advanced I return 403. But here I found the workaround which ruins this system:

  • User activates advanced mode
  • He performs any action and save its access token
  • He disactivates advanced mode
  • Now he's able to perform actions just like he is in advanced mode with this token, however he actually has not permissions to do it.

I'm trying to find some fine solution for this situation but I have no ideas except set 1 minute timeout or always check AspNetUserClaims instead of cookie and so on, but they don't work in my case because he can activate a lifetime feature in this one minute interval and then use it forever.

But i'd like to set some server-side flag like oops, this guy have just changed his cookies, check it from database or something to lower database roundtrips for common API calls.

Is there any standard default way to do it? Or maybe I have just chosen a wrong instrument?

Alex Zhukovskiy
  • 9,565
  • 11
  • 75
  • 151
  • In Identity you can simply logout the user and log them back in programmatically. Seems like that would bounce their cookie and IPrincipal – Eonasdan Jan 16 '18 at 14:30
  • @Eonasdan I don't know when to logout the user. For every request? Every 5 minutes? If the former then it's just useless. If the latter than user has 5 minutes to do everyting. – Alex Zhukovskiy Jan 16 '18 at 14:59
  • "User activates advanced mode" -> bounce login -> "He disactivates advanced mode" -> bounce login – Eonasdan Jan 16 '18 at 15:05
  • bounce login doesn't affect already existing cookie. If user has a value with `val1,val2,val3` and it send it to the server, `Claims` says that he actually has them. I can't log out these values on the server side. It's just a request. – Alex Zhukovskiy Jan 16 '18 at 15:07
  • Simplified: server receives some request, let's say "pring message on home page" with cookie "HOME_PAGE_MANAGER". What server should do in this case? – Alex Zhukovskiy Jan 16 '18 at 15:08
  • but can't you still bounce the login and do User.Claims.Any(x => x....Type = "HPM") when you receive the request? Doesn't the logout action kill the cookie? – Eonasdan Jan 16 '18 at 15:11
  • @Eonasdan no, it doesn't. And this is the very reason for writing this whole question. – Alex Zhukovskiy Jan 16 '18 at 15:15
  • have you seen this: https://stackoverflow.com/questions/34020730/invalidate-old-session-cookie-asp-net-identity – Eonasdan Jan 16 '18 at 15:58
  • @Eonasdan to be honest, I don't recall, it was 6 months ago. Thank you for your post, but it either doesn't work or I haven't seen it. Anyway, thanks for a link. – Alex Zhukovskiy Jan 16 '18 at 16:00
  • lol wow sorry I didn't notice the time lapse, this was on top of my question list when I logged in. – Eonasdan Jan 16 '18 at 16:04
  • @Eonasdan no problem, you're welcome :) As I read your link, they just propose to reduce cookie lifetime. It's not an answer, really. – Alex Zhukovskiy Jan 16 '18 at 16:10

2 Answers2

0

You Need to send update cookies according to your claim value.

Below is code to update your claim value.

Inside your action when user disable/enable advanced mode, Then update user claims.

var isAdvanced= "1";    

var identity = (ClaimsIdentity)User.Identity;

// check if claim exist or not.
var existingClaim = identity.FindFirst("IsAdvanced");
if (existingClaim != null)
    identity.RemoveClaim(existingClaim);

// add/update claim value.
identity.AddClaim(new Claim("IsAdvanced", isAdvanced));

IOwinContext context = Request.GetOwinContext();

var authenticationContext = await context.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ExternalCookie);

if (authenticationContext != null)
{
    authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(identity,authenticationContext.Properties);
}

As soon as you will made a redirection, you will get your get updated claim value, hence you don't need to make database round trip.

Credit to this post.

Amit Kumar
  • 5,888
  • 11
  • 47
  • 85
  • But what's about saved request? For example he's just used chrome feature `save as cUrl` and then manually sent the request? It still have a cookie with this value. I remove it for next requests but it doesn't matter, because I check nothing on server side. Or am I? – Alex Zhukovskiy Oct 06 '17 at 10:43
  • @AlexZhukovskiy : No, It won't work if you send request manually with old cookies. because old cookies value still have claim value "1" . – Amit Kumar Oct 06 '17 at 10:56
  • @AlexZhukovskiy : What is cookie token expire time ? – Amit Kumar Oct 06 '17 at 10:58
  • Currently it's about 1 day, but it doesn't matter. As i said above, even 1 minute cookie is already dangerous. – Alex Zhukovskiy Oct 06 '17 at 11:15
  • 1
    @AlexZhukovskiy : I had similar situation but my data is not that much sensitive. it's just my opinion. after user login , when user changes mode then before updating claim, you keep old cookie value in `cache` `dictionary ` `key =cookie` & `value=current+1day` . now create an `ActionFilter` and check cookie value of incoming req, if it exist in `cached` dictionary` then block that req. And as your expiry time is one day so you can remove val from dic whose value less than current date time. – Amit Kumar Oct 06 '17 at 11:34
0

Unfortunly, the only way I found is actually query DB itself and check if user has valid credentials:

public bool HasRequiredClaims(string[] requiredClaims)
{
    using (var context = new ApplicationDbContext())
    {
        int actualNumberOfClaims = context.Users
            .SelectMany(x => x.Claims)
            .Count(c => requiredClaims.Contains(c.ClaimValue)); // claim values are unique per user (in my case) so I don't have to filter on user
        return actualNumberOfClaims == claimsValuesToSearch.Length;
    }
}
Alex Zhukovskiy
  • 9,565
  • 11
  • 75
  • 151