Azure Active Directory Authentication And Authorization For Webapps
tl;dr
Authentication and Authorization are really easy to configure in Azure Web Apps using Azure Active Directory.
- Activate authentication in your web app: Authentication / Authorization → On → Azure Active Directory → Express → OK →
Log in with Azure AD
→ Save - Create roles in your application: go to your Azure AD → App registrations → Select the app you created in 1) → Edit Manifest → Add roles with unique ids → Save
- Assign roles: in Azure AD, select Enterprise applications → Select your app → Select Users and Groups → Add → Select a user or group → Select one of the roles you created in 2) → Save
- Use roles in your application: get token from header
x-ms-token-aad-id-token
→ decode JWT token → use fieldroles
Context
We’re using Auth0 as our BaaS authentication provider. Awesome as it is, one of their limitations ironically is that the authorization scheme for administrators is a bit limited, and getting access to their admin panel basically makes you god1. Our customer support agents need to be able to debug customer’s accounts, and perform operations such as impersonation, but we don’t want them to mess with our configuration.
This is a perfect use case for a small admin console hosted on a web app and secured via Active Directory. The following will let you configure Active Directory for authentication (verifying who the user is) and authorization (assessing what they can do) on an Azure App Service. This is very simple for the value it brings.
Let’s assume that you created and deployed a web app. I’ve created a basic node app here that basically displays your headers, and your authorizations if you’re authenticated.
Activating Active Directory authentication
This step is easier than what you could wish for when thinking security, provided that you’re respecting the core pre-requisite: your enterprise AD is synchronized with Azure AD.
Start from your WebApp, and then under Settings, navigate to the Authentication / Authorization
blade. From here, turn the toggle to On
, then select Azure Active Directory
.
Select Express
, create a new application, then press OK
.
Back at the AD authentication screen, make sure you select Log in with Azure Active Directory
in the dropdown, then press save.
That’s it! Next time a user tries to access the webapp, they’ll be greeted with an AAD login screen, (unless they’re already logged in to Office 365 or something else using AAD, in which case SSO does its awesomeness). As far as authentication goes, this is the further you really need to go.
On their first login, they’ll be asked to authorize the app to access their information.
Accessing user basic information
Authentication also adds a cookie to your client, which allows for all subsequent queries to be authenticated transparently. From then on, AD headers are added to every request sent to the WebApp. They let you gather some information about the user:
The header x-ms-token-aad-id-token
contains the OAuth token, which gives you a bit more about the user:
{
"aud": "be6ffc7f-bc2a-42ff-98d5-5b2b6cf72e9e",
"iss": "https://sts.windows.net/437426e6-c3c0-4806-8921-76bcdd4493c9/",
"iat": 1491258820,
"nbf": 1491258820,
"exp": 1491262720,
"amr": [
"pwd"
],
"email": "charles.feval@xxxxxxxxxx",
"family_name": "Feval",
"given_name": "Charles",
"idp": "live.com",
"ipaddr": "66.47.123.78",
"name": "Charles Feval",
"nonce": "6d4615cf389546e0d2e8e3327ff5526f_20170439824338",
"oid": "abacdce6-bcfe-4ea1-90d9-55114f2d7756",
"platf": "5",
"sub": "pg6ZyWh5DKq0BwTlWVjxrhdRB8i0O-SOPKntvc8g878",
"tid": "437426e6-c3c0-4806-8921-76bcd81893c9",
"unique_name": "live.com#charles.feval@xxxxxxxxxx",
"uti": "s5SHUSnpekiwmA_aii290AA",
"ver": "1.0"
}
Some of these values are described here. For all intents and purposes, basic information is everything you need.
You can go crazy with these and add some traceability, or retrieve data associated with that user. But to the core, you’re only preventing anyone outside of your organization to connect to your application, which is already nice, but not necessarily enough for enterprise security. For that, we need to resort to authorization.
Authorizing users with Active Directory
Authenticating is nice, but it doesn’t tackle more complex permissions. What we want to do next is define a set of permissions that we can use in the application. There are mainly two ways you’ll be able to authorize a user:
- by looking at which groups they belong to, which is a quick but kinda dirty way of doing it - you may want to use that to do something simple where you give access to members of the
admin
group only. - through roles, which is a bit cleaner since it creates a configuration layer between the application and your AD. This will allow you to do RBAC, i.e. you will be able to add roles to existing AD groups (or new groups which you’ll add existing groups to).
You can implement either or both solutions. Both start with the same steps: go to the Active Directory blade in the Azure Portal, then select App registrations
in your AD, and select the app you created just before.
Then click the Manifest
button, which brings a JSON editor:
Then do either or both of the following:
Getting the user’s groups in the token
We will add groups
to the scope for OAuth authentication. This in turn will add a groups
claim to the token we receive. In the manifest, locate the groupMembershipClaims
, which should be set to null
, then update to the following:
"groupMembershipClaims": "SecurityGroup"
and save. You can skip the next part if you don’t care about roles.
Do it well and use roles
In the manifest file, locate the appRoles
node (it should be near the top). Add as many roles as needed2:
- allowedMemberTypes can be a combination of
User
andApplication
. The latter is for authorization of other apps, you probably don’t need it as part of this post’s scope - displayName is what it’s going to show in the configuration screen. It can be anything, easier if unique
- id needs to be a unique Guid that you won’t be able to change. Generate a new one
- isEnabled definitely doesn’t activate or deactivate the role.
- description is a biography of the author
- value is what you’re gonna get in your application
"appRoles": [
{
"allowedMemberTypes": [
"User"
],
"displayName": "Little User",
"id": "c366e905-970a-4457-a5d2-b17977e2373e",
"isEnabled": true,
"description": "Can barely do anything.",
"value": "LittleUser"
},
{
"allowedMemberTypes": [
"User"
],
"displayName": "Super admin",
"id": "fb73fccd-f420-45f9-ac93-bfbb9f6416a7",
"isEnabled": true,
"description": "Can do super powerful admin stuff. Sucks not to be in.",
"value": "SuperAdmin"
}
]
and save. Then, still in the Active Directory blade, select the Enterprise applications
, then in Manage
select All applications
, then pick your application.
Then select Users and groups
, and click Add
.
Then select the user or group3 you want to grant access to, and start assigning roles.
Don’t forget to save, then observe the result of your work with pride:
Using authentication
It’s now time to put your newly acquired knowledge in practice to accomplish wonderful things. We’ll just list your groups and roles. It’s already wonderful as it is.
If you previously logged in to the application, you’ll need to log out and log back in order to generate a new token. After logging in again, here is what the decoded token now looks like:
{
//...
"groups": [
"6d1ce122-e877-448b-b617-21233cbd6982",
"ac4445bb-4d7e-4d8f-b496-d9b3a13b31d5"
],
//...
"roles": [
"SuperAdmin"
],
//...
}
Victory!
Obviously you will see groups
only if you added the group scope, and roles
if you added roles to your user.
With some very simple code we can decode the JWT token4, and look at those groups and roles (full code is here):
const jwtDecode = require("jwt-decode");
app.get("/", (req, res) => {
for (var name in req.headers)
res.write(`${name}: ${req.headers[name]}\n`);
var token;
token = req.headers["x-ms-token-aad-id-token"];
if (token)
{
var token = jwtDecode(token);
if (token.groups)
token.groups.forEach(group => res.write(`You are part of group ${group}\n`));
if (token.roles)
token.roles.forEach(role => res.write(`You have role ${role}\n`));
}
res.end();
});
Then by going to the application, here is the result:
In real life, you would use these roles or groups to show or hide operations, and authorize or prevent them on the API.
There you go. Authentication and authorization infrastructure set up in a few minutes, all you need to do now is:
- Figure a way to run that locally (tip: inject your token and/or roles)
- Implement the security inside your application (tip: this is probably where security holes will occur, be careful out there).
Notes
-
Admittedly a very shi**y god who can just change keys and delete users. ↩
-
You would typically add a role anytime a logical group of actions can’t be done by a subset of users. Better have more roles than not enough. There’s a whole school of ideas on how to configure roles properly (overlap or not, positive or negative, etc.), I’ll let you figure this out. I don’t have the answer. I don’t have the answer to a lot of other things, as it turns out. ↩
-
You can’t select groups in the free version of AAD, as the yellow-brownish message makes very obvious. ↩
-
There’s little need to validate the JWT in this use-case, since this is generated by the AAD layer and stored in a server-side encrypted cookie ; but one is never too cautious when it comes to security, apparently. ↩