About a week ago I announced the Microsoft Authentication Graph Helpers. Since then, I decided to publish a small, but a useful enhancement to it - AzureAdAuthorizationAttribute and AzureAdAuthorizationHandler!
Imagine the following: When you are writing an Azure AD backed application, you should leverage of few following principles for authorization:
- Azure AD default roles (Global Administrator especially)
- Azure AD groups (because not everyone has Azure AD Premium)
- Azure AD application roles
Microsoft's 1st party applications leverage the default roles - so if you are a global administrator, you will have admin access to the application, but most 3rd party apps don't. Then there is the groups vs roles. In our applications, what we do is we implement the group assignment for everyone and the application roles get assigned behind the scenes by a WebJob (I will write a post about that in the future). Groups have been and still are (and likely to be) the unified security unit, so let's leverage them.
At the moment, if you want to leverage group authentication - most guides tell you to have all the groups passed to you in the id_token. This is great, but wait - the groups are passed as claims, that means they are likely to end up in user's cookies! If you are dealing with a small company, it may not be a problem at all, because people will not be in more than 20 groups, but what if the customer is a large corporation where every user is member of a large amount groups? Not that just you will have to carry the groups either in your token or in the session store... Or if the amount of group is just too many - are you going to pull them from Microsoft Graph (or Azure AD Graph) _claim_source endpoint? Do you really need to store all those group in your user's session/cookies, because the application needs like 10 groups? And last and foremost - what if the user is removed from the group while using the application - will they loose access immediatelly or will they session need to expire first and new login triggered (this can be actually mitigated by using CookieAuthentication's OnValidatePrincipal event)? Most of the answers are likely to be no.
So how to do it properly (this is my personal "properly" view and I am sure there are other ways too)?
ASP.NET Core has this great feature called Authorization Handlers. These handlers allow you to perform authorization anywhere in the code, including view-based authorization which is pretty cool! So what we decided to do was to create such handler for Azure AD - the handler accepts both Azure AD groups and roles as parameters.
Sidenote: Implementing Azure AD Roles in multi-tenant apps
Since every role has it's own unique ID in Azure AD, the only common identifier for the role is its roleTemplateId which specifies the template from which the role has been activated. So in order to fetch roles, you need to do three things - get all the roles in the tenant, identify the correct one by its roleTemplateId and then pull all role members and verify whether the desired user is a member of that role.
From there, we also decided to implement an attribute which you can easily use to decorate your controllers and methods, to protect them very easily.
Sidenote on performance: At the moment, all lookups to Microsoft Graph are real-time. As we proceed further with making the library more production ready, there will be optional caching implemented by using ASP.NET Core's in-memory cache.
Sidenote on roles: For performance sake, we currently only pull the first page of role members, so in case you were pulling the the GuestMembers role etc. you would have to count-in paging.
Leave a Comment
Your email address will not be published. Required fields are marked *