Identity vs Permissions

We often see people misusing IdentityServer as an authorization/permission management system. This is troublesome – here’s why.

IdentityServer (hence the name) is really good at providing a stable identity for your users across all applications in your system. And with identity I mean immutable identity (at least for the lifetime of the session) – typical examples would be a user id (aka the subject id), a name, department, email address, customer id etc…

IdentityServer is not so well suited for for letting clients or APIs know what this user is allowed to do – e.g. create a customer record, delete a table, read a certain document etc…

And this is not inherently a weakness of IdentityServer – but IdentityServer is a token service, and it’s a fact that claims and especially tokens are not a particularly good medium for transporting such information. Here are a couple of reasons:

  • Claims are supposed to model the identity of a user, not permissions
  • Claims are typically simple strings – you often want something more sophisticated to model authorization information or permissions
  • Permissions of a user are often different depending which client or API it is using – putting them all into a single identity or access token is confusing and leads to problems. The same permission might even have a different meaning depending on who is consuming it
  • Permissions can change over the life time of a session, but the only way to get a new token is to make a roundtrip to the token service. This often requires some UI interaction which is not preferable
  • Permissions and business logic often overlap – where do you want to draw the line?
  • The only party that knows exactly about the authorization requirements of the current operation is the actual code where it happens – the token service can only provide coarse grained information
  • You want to keep your tokens small. Browser URL length restrictions and bandwidth are often limiting factors
  • And last but not least – it is easy to add a claim to a token. It is very hard to remove one. You never know if somebody already took a hard dependency on it. Every single claim you add to a token should be scrutinized.

In other words – keep permissions and authorization data out of your tokens. Add the authorization information to your context once you get closer to the resource that actually needs the information. And even then, it is tempting to model permissions using claims (the Microsoft services and frameworks kind of push you into that direction) – keep in mind that a simple string is a very limiting data structure. Modern programming languages have much better constructs than that.

What about roles?
That’s a very common question. Roles are a bit of a grey area between identity and authorization. My rule of thumb is that if a role is a fundamental part of the user identity that is of interest to every part of your system – and role membership does not or not frequently change – it is a candidate for a claim in a token. Examples could be Customer vs Employee – or Patient vs Doctor vs Nurse.

Every other usage of roles – especially if the role membership would be different based on the client or API being used, it’s pure authorization data and should be avoided. If you realize that the number of roles of a user is high – or growing – avoid putting them into the token.

Conclusion
Design for a clean separation of identity and permissions (which is just a re-iteration of authentication vs authorization). Acquire authorization data as close as possible to the code that needs it – only there you can make an informed decision what you really need.

I also often get the question if we have a similar flexible solution to authorization as we have with IdentityServer for authentication – and the answer is – right now – no. But I have the feeling that 2017 will be our year to finally tackle the authorization problem. Stay tuned!

This entry was posted in .NET Security, IdentityServer, OAuth, OpenID Connect, WebAPI. Bookmark the permalink.

31 Responses to Identity vs Permissions

  1. gep13 says:

    I understand the suggestion to acquire authorization data as close to the operation as possible, i.e. the user clicks the “Add” button, and in the API for the Add action, before doing any work, you check to see if the current user is authorized to take that action, and if not, return the appropriate HTTP Code.

    What happens though when you want to show/hide UI elements in the application based on the permissions associated with the user? i.e. you may want to disable the Add button, or hide it completely, if the current user isn’t allowed to take that action. Historically, we have used claims added into the token to control showing/hiding elements on the screen once the user has been authenticated. What are your thoughts on this area? Thanks!

  2. applicita says:

    I wish I had this knowledge back in 2012, it took me a while to come to the same conclusion, I was Claims mad back then!

  3. Felix says:

    Do you expect that authorization solution will treat “Roles” in ASP.Identity store similar to Groups? In broader sense, Group may have claims, similar to users – and users will have some easy way to aggregate the claims of the groups they belong to. Then protected securable may match required claim to the claims that users have by virtue of belonging to Groups. Right now for me this is the hardest problem to solve…
    Thank you

  4. You would think this would be obvious? Your suggestion is the correct suggestion and fails inline with the SRP (Single Responsibility Principle).

  5. Diego says:

    We have had similar dilemmas and I although I agree with what you say about separating clearly those concerns here’s what we did (and for our scenario it has some benefits and the downside is not that relevant).

    Our Identity Server keeps identity details such as name, email, dob, etc. But we decided to also include “authorization” details disguised as “department” identities (call it “role”?).
    For example as part of the identity we have something like:
    “departments”: [“read-only”, “editors”, “admins”]

    That seems like mixing authorization with identity, but we thought that it’s actually not much different to saying:
    “departments”: [“departmentA”, “departmentB”, “departmentC”]
    because the fact that a user has the department “read-only” doesn’t say anything until you put it in context with the http verb and resource trying to access, and that still happens only at the resource’s API end.

    The big benefit we found by including those roles is that at the client side we can also have authorization based on those token’s “identities” in order to display or hide certain UI elements without the need to make an additional call to the API to determine whether that identity is authorized to view some UI elements. For example the UI will display the administration section in browser if the id_token has the “admins” department because it “trusts” the token it just requested, or hide EDIT button on some areas if no “editors” department found.

    Then at the API level we have our rules to say that for example a POST on some resource if the access_token has no “admins” department will be unauthorized.

    The downside is what you mention about those “identities-permision-ish” changing and the possibility that third party clients could have an old “identity”. We decided to violate this separation of concerns because the client application is also our application and we silently renew tokens every 5 minutes (we can live with those 5 minutes of incorrect identities). I understand that could become a problem if we ever decided to expose our resources to third party companies.

    Another downside is the size of the token. So I agree with all your points but it’s a bit of a grey line :)

    • Tamas Føldesi says:

      Where did you get the access token from? The one I have been getting from Identity Server 3 has the audience set to IS self, so it doesn’t seem right to use it for accessing another API. Or are you sending the id token as access token?

  6. Mark says:

    The sentence “But I have the feeling that 2017 will be our year to finally tackle the authorization problem” has really peaked my interest. It can be a really complex problem to solve when moving beyond RBAC. Most complex projects I’ve encountered required some attributes of both user and resource (ABAC) to determine authorized access but this can mean hard-coded rules, which may or may not be a problem.

    Are you thinking along the lines of ABAC, XACML or something else entirely?

  7. Tamas Føldesi says:

    I have been trying to set up an architecture that involves a purely browser based client, an API, and security token service. Reading the docs here: https://identityserver.github.io/Documentation/docsv2/overview/bigPicture.html suggests that Identity Server is a good candidate for doing authentication and authorization in one round trip – but in this article you seem to say the opposite…?
    The linked doc made me try using the IS accces token to secure my API, though it doesn’t seem right, the audience on that is IS, just to name one problem.

    • IdentityServer supports both id_tokens (authentication) and access tokens (access to APIs) – some people also call that (coarse grained) authorization for clients.
      This article talks about (finer grained) authorization for users.

      The audience is “issuer name + /resources” – or IOW “all resources guarded by this issuer” – for more fine grained audience – the scope claims are used.

      • Tamas Føldesi says:

        Okay, thanks for clarifying – to me the article sounded like I should not be using IS to do authorization at all, but then I guess using it to grant access to a certain web API as a whole is fine.

        Also, I was not aware of what /resources means, but with that I have one questionmark less :)

    • “just to name one problem” – what other problems do you have.

      For issues with identityserver, the best place is the github issue tracker.

  8. Anthony says:

    Is the “AspNetRoleClaims” table (part of the Asp.net core Identity EntityFramework implementation) what you are describing permissions using claims should NOT be modeled using or just “AspNetUserClaims” ? (i.e. a rich claims-based models).

    The organisation I work in, typically delegates all management of permissions to one service desk. The ideal being that the Service desk user would open up a single management application to manage users, roles and permissions (roles are effectively templated groups of permissions in this organisation). They wouldn’t want to open up one application for users and roles and then individual applications to manage permissions. So, the description and association of the permission to a user could be managed at a higher level (in the Asp.net core Identity EntityFramework implementation for example) but the implementation should be at the lower level (within the application requiring authorization). This would then lean towards creating a Permissions & RolePermissions table to avoid using the Claims based tables. This blog post talks about a similar scenario: http://benjamincollins.com/blog/practical-permission-based-authorization-in-asp-net-core/

    MS does seem to push you down the claims based route. It feels like the easy option but isn’t a good fit as you say. Looking forward to you solving Authori(z/s)ation in 2017, but I may have implemented something already !

  9. Mohamed says:

    (Just started working with IdServer)

    My confusion came from the fact that a client has scopes (“Add”, “Remove”, ect…) so I expected I could do similar work with a user. The final scope would be an intersection of the Client + User.

    Not sure if this makes sensse :-)

  10. Matt says:

    Re. the point about modelling permissions with claims – Microsoft certainly do more than push you towards this, they explicitly say claims can/should be used for authorisation: https://docs.microsoft.com/en-us/aspnet/core/security/authorization/claims.

    I find it hard to disagree with what they’re saying in the above, it isn’t breaking separation of concerns – the idp is only returning identity info and the API is using that as it sees fit. Issues around size of cookies and token expiration are kind of separate to my mind, we can’t avoid them but they’re practical issues that shouldn’t affect the concepts.

    Obviously it has to be the API that’s making the authorisation decisions specific to its own requirements. And I agree that in many cases individual APIs may well have more info about a user that makes no sense to be stored in the idp but in a simple case I don’t see any issue with an API authorising access based only on what claims the idp has reasonably supplied.

    • I didn’t say “don’t use claims for authorization” – if your authorization is based on identity data, that’s totally fine.

      I am saying that claims should be used to model identity – not permissions.

      • Jimmy says:

        So let’s make it clear:

        A claim “date of birth” with value “1/1/1970” is absolutely fine. Because it models “identity”.

        A claim “night_club_access” with value “true” is wrong. Because it models “permissions”.

        If your ecosystem with 150 different clients want to decide whether a user has access to a nightclub or not, they should decide on their own, based on user’s identity, and specifically using the “date of birth” claim.

        A client that operates in a country where legal age to enter nightclub is 18 may produce a different result from another client that operates in a country where legal age is 21. If we had modeled permissions, a single “night_club_access” would be problematic all the way.

        Correct?

      • Yes – similar to your passport – it contains a date of birth claim – and you can use it in many countries. The exact policy when you are allowed to drink alcohol or enter a bar, is handled locally.

      • Jimmy says:

        Hello Dominick, thank you for the response in my previous comment. I am a bit confused as this post seems to me that contradicts with an example from the official documentation. The following code from official documentation seems to me that models *permissions* rather than *identity*:

        http://docs.identityserver.io/en/release/topics/resources.html

        // this API defines two scopes
        Scopes =
        {
        new Scope()
        {
        Name = “api2.full_access”,
        DisplayName = “Full access to API 2”,
        },
        new Scope
        {
        Name = “api2.read_only”,
        DisplayName = “Read only access to API 2”
        }
        }

        These two scopes seem to me like “permissions”. If Api2 wants to PERMIT full access or not it’s Api2’s internal business, and should not be a part of the IDENTITY. In other words, in this example IdentityServer seems to carry (and control!) information about Api2’s PERMISSIONS. At least that’s how i interpret it. Can you please elaborate on this?

        By reading and (i think) understanding your post, i would expect just a simple “api2” scope on the identity, and do not care about “full_access” or “read_only” as this is api2’s business and this has nothing to do with identity per se.

        And also, a BIG thanks to you, Brock and the rest of the contributors for a great project ***AND*** a great documentation!

      • Scopes are not about user permissions – they are about what access a user grants to a client.

        I know it is a bit confusing – but makes total sense once you understand it ;)

        Maybe read this: https://www.amazon.com/OAuth-2-Action-Justin-Richer/dp/161729327X/ref=sr_1_1?ie=UTF8&qid=1496135709&sr=8-1&keywords=oauth2+in+action

  11. Vishal Rastogi says:

    I’m a bit confused. I use roles to control permissions all over my api using the authorize attribute. I am also using reference tokens. If the access token doesn’t have the claims for roles, how would the api know if the user is authorized to access a controller? Hit the DB again to check roles for the user? And what if the api doesnt have access to the user database?

    • Well – I think I already said everything in my post.

      Your API should def have access to the data store where it can find its authorization information.

      If you system is that simple that a couple of roles is all you need, maybe you then can ignore my advice – you will regret that later then when complexity rises.

      • CodeRevver says:

        Hi Dominick,

        I enjoyed the post. I’m trying to come to terms with how I should structure permissions in my app and there’s lots of conflicting advice out there.

        I have a question related to the above though.

        Right now I can have 100 actions/permissions assigned to a role, and a user assigned to the role. It’s fairly easy to check if the action I’m trying to do (“CreateUser”) is available on any of my roles, therefore allowing me access. What makes this approach inferior to a completely separate implementation not using roles?

        Also what kind of implementation would you suggest? The only thing I can think of is almost identical do the above, just kept away from the Identity DB.

        e.g. These tables: Permissions, UserGroups, UserGroupPermissions, UserUserGroups.

        Custom Authorize attribute would then see if the permissionid with the action you’re executing is contained on any of the groups you are associated with – Pretty much exactly the same as roles, except without the claims.

      • Technically you can implement that in many ways. I think my main message was “don’t mix identity with permissions – especially not in security tokens”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s