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.

19 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.

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