Role/Group based Authorization using ADAL.js and ASP.NET Web API - azure-active-directory

What we are planning to achieve is a role-based security for a Front end Angular-2 and back-end ASP.NET Web API application. We are doing the authentication process with the help of ADAL.js and storing the token in the local storage. We have also implemented the approach shown here i.e. to call the Graphi API and get the user groups to stuff them into Claims.
My question is : Is there anyway, we can add role claims from server to the bearerToken which is residing in the local storage. Or is there any better way to approach this issue.

The code sample mentioned assign the role based on the group. If you have the Azure AD basic version, it support to assign the role to the users/groups directly.
My question is : Is there anyway, we can add role claims from server to the bearerToken which is residing in the local storage. Or is there any better way to approach this issue.
Yes, it is possible. To issue the role claims we need to assign the users to assign the roles to users or group first. Then when the user acquire the token, the Azure AD would issue the relative role claims in the token.
You can refer the code sample for using the role claim from here.
And you may also be interest in groups claim developing.

ok i was struggling with this for a while and i have figured it out i believe.
First,
In Azure AD, set up your WebApi app as application type to be Web App / API.
go to Manifest file and add your roles like
[
{
"allowedMemberTypes": [
"User"
],
"displayName": "Reviewer",
"id": "0238c2bb-9857-4d07-b760-a47ec621d57a",
"isEnabled": true,
"description": "Reviewer only have the ability to view tasks and their statuses.",
"value": "reviewer"
},
{
"allowedMemberTypes": [
"User"
],
"displayName": "Approver",
"id": "000018cb-19e3-4f89-bf99-5d7acf30773b",
"isEnabled": true,
"description": "Approvers have the ability to change the status of tasks.",
"value": "approver"
}
]
Then create the the Client app as Application type to be Native app and add required permissions to the service you added above.
In the SPA Angular app add something like this
var endPoints = {
// "https://localhost:44386/" is the API URL
// "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx" is the Service Application ID
"https://localhost:44386/": "xxxxxxxxxxxxxxxxxxxxxxxxxx"
};
adalAuthenticationServiceProvider.init({
instance: "https://login.microsoftonline.com/",
// tenant is your tenant name (something like below)
tenant: "{NAME}.onmicrosoft.com",
// this is the Native app application ID (ClientID) you registered
clientId: "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
extraQueryParameter: "nux=1",
endpoints: endPoints
}, $httpProvider);
}
]);
Then, in your startup.cs you need to set up the Service App like the following:
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
TokenValidationParameters = new TokenValidationParameters
{
/* "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx" is the Service Application ID. (Same as you registered in the client app above)*/
ValidAudience = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
RoleClaimType = "roles"
},
/*enant is your tenant name (same as you registered in client app above)*/
Tenant = "{NAME}.onmicrosoft.com"
});
Finally you need to go to Azure active directory => Enterprise application => all applications => select your webAPI service => Users and groups => then assign users to the roles.
When this is all done when you log through your client app to authenticate and call the webapi, adal.js and ada-angular.js will put the proper bearer token that contains roles

Good to learn this approach.
Ted, thanks for sharing your solution !
For those who are not familiar with operating Azure AD manifest file. The following is a good resource.
https://thinkthencode.wordpress.com/2016/04/24/azure-ad-using-app-roles-for-authorization/
"appRoles": [
{
"allowedMemberTypes": [
"User"
],
"displayName": "Reviewer",
"id": "0238c2bb-9857-4d07-b760-a47ec621d57a",
"isEnabled": true,
"description": "Reviewer only have the ability to view tasks and their statuses.",
"value": "reviewer"
},
{
"allowedMemberTypes": [
"User"
],
"displayName": "Approver",
"id": "000018cb-19e3-4f89-bf99-5d7acf30773b",
"isEnabled": true,
"description": "Approvers have the ability to change the status of tasks.",
"value": "approver"
}
]

Related

Adding application permission to an client application using MS Graph SDK - Java

I have registered an application with name "API" and for this application I added two App Role in Manifest
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"description": "Demo Role for application.",
"displayName": "Demo Role",
"id": "42ee481e-b4bc-4afa-9499-586bc2a079be",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "demo.role"
},
{
"allowedMemberTypes": [
"Application"
],
"description": "Test Role for application.",
"displayName": "Test Role",
"id": "42ee481e-b4bc-4afa-9499-586bc2a079bd",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "test.role"
}
]
Now I have registered another app called "Client Application" and I want to assign this application above two app roles which I defined in application "API"
From searching the docs I found that we assigning permissions is done to the application service principal, not the Application object.
https://learn.microsoft.com/en-us/graph/api/serviceprincipal-post-approleassignedto?view=graph-rest-1.0&tabs=java
Here is the sample code that I found in the documents...
AppRoleAssignment appRoleAssignment = new AppRoleAssignment();
appRoleAssignment.principalId = UUID.fromString("33ad69f9-da99-4bed-acd0-3f24235cb296");
appRoleAssignment.resourceId = UUID.fromString("9028d19c-26a9-4809-8e3f-20ff73e2d75e");
appRoleAssignment.appRoleId = UUID.fromString("ef7437e6-4f94-4a0a-a110-a439eb2aa8f7");
graphClient.servicePrincipals("9028d19c-26a9-4809-8e3f-20ff73e2d75e").appRoleAssignedTo()
.buildRequest()
.post(appRoleAssignment);
principalId: The id of the user, group or client servicePrincipal to which you are assigning the app role. -- Where can I find principalId? Is this the objectId of the "Client Application"?
resourceId: The id of the resource servicePrincipal which has defined the app role. Where can I find resourceId? -- Is this the object ID of "API" Application which has app roles defined?
appRoleId: The id of the appRole (defined on the resource service principal) to assign to a user, group, or service principal. -- I found the app role id - the guid mentioned in app role in manifest of "API" application.
Please assist.
Principal id is the ObjectId but not the actual one which you see in portal but it is the object Id of the service principal and similarly resource Id is objectId of serviceprincipal of resource app.
See the difference from below provided info:
Portal:resourceApp:
App id:51100c0f-xxxx-xxxx-xxxx-xxxxxxxa59,
ObjectId:2275ec6c-xxxx-xxxx-xxxx-xxxxxx2b76e
From powershell commands:
$ResourceId= (Get-AzureAdServicePrincipal -Filter "displayName eq 'testapp'").ObjectId
#output :ResourceId: 36929cef-xxxx-xxxx-xxxx-xxxx989dac7
Portal:client app:
App id:329cd48e-xxxx-xxxx-xxxx-xxxxxxxx2b88,
objectId:9d5e7540-xxxx-xxxx-xxxx-xxxxxxxxx9e1
$principalId=(Get-AzureAdServicePrincipal -Filter "displayName eq 'client app'").ObjectId
#Output : Principal id: 85a53d22-xxxx-xxxx-xxxx-xxxxx300b2
In an app role assignment, resourceId is the id of the servicePrincipal for the resource app ( API app in your case). To find the id of a service principal for which you know the appId through graph api for resource app and client app:see the below request
GET https://graph.microsoft.com/v1.0/servicePrincipals?$filter=appId eq '{app-id}'
(or)
GET https://graph.microsoft.com/v1.0/servicePrincipals/<App id>
Or
GET https://graph.microsoft.com/beta/serviceprincipals?$filter=startswith(displayName, 'Application-Name')
The response will contain the id property of the servicePrincipal object, and you can use that when creating the app role assignment.
Reference:
graph/api/serviceprincipal
When you create/register an application from portal UI, a service principal is created for you automatically however it is not the case when you create application using API calls.
When you register an application using API, you will have to explicitly create service principal for that application...
to create a service principal you need a graph client and object Id of the application you created (please note object Id and app client id is diff) - you can see your object Id from portal UI.
Application application = graphClient.applications(objId)
.buildRequest()
.get();
ServicePrincipal servicePrincipal = new ServicePrincipal();
servicePrincipal.appId = application.appId;
graphClient.servicePrincipals()
.buildRequest()
.post(servicePrincipal);
Now you have created a service principal for you application...next up is finding the ID which you can do like this...
ServicePrincipalCollectionPage servicePrincipals = graphClient.servicePrincipals()
.buildRequest()
.get();
List<ServicePrincipal> list = servicePrincipals.getCurrentPage();
Go through the list and choose your application based on display name, and you can find ID too.
ServicePrincipal.Id --> This is what you need...
and based on your need...
The principalId is the client app which you created - so you need servicePrincipal.Id of that application.
The resourceId is the app where you defined your approles - so you need servicePrincipal.Id of that application.

Managed Identites and AppRoles in Token

I have a Web API "App Registration" called "BackEnd_API" which defines some Application Roles and User Roles.
{
"allowedMemberTypes": [
"Application"
],
"description": "resource.READ allows you read access to all items in the application",
"displayName": "resource.READ",
"id": "9650cfb9-570d-4b79-1337-a01337ed6c29",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "resource.READ"
},
I then have another Client Application "App Registration" called "Client_App" which consumes that API to which i've assigned the AppRoles "resource.READ" using either Azure_CLI or PowerShell.
In the Azure Portal I can see that the Service Principal is assigned the role.
When i use the Client_Credentials Flow the resulting access token DOES contain that Roles claim which i use on the BackEnd to authorize the caller.
Until Here ALL Good.
Now, I want to consume the same Web API "BackEnd_API" from another Consuming Application using Managed Identities. So I've created another "App Service", enabled System Assigned Identity and assigned the AppRoles "resource.READ" using Azure CLI.
In the Azure Portal I can see that the Service Principal is assigned the role.
I can get a Token using the JS Azure SDK.
var withClientSecretCredential = () => {
require("#azure/core-auth");
require('dotenv').config()
const {
ManagedIdentityCredential
} = require("#azure/identity");
const logger = require('#azure/logger');
logger.setLogLevel('info');
// Load the .env file if it exists
const credentials = new ChainedTokenCredential(
new ManagedIdentityCredential("54e5c672-872f-4866-b067-132973cb0c91"),
);
token = credentials.getToken(['api://e22fd9eb-3088-4155-936a-0919681c3eb5/.default']);
return token
But the received token in this case has no 'role' claims, so the API call fails to authorize.
I double checked roles and assignment all looks good; is this supposed to work ?
Token without 'role' claim.
{
"aud": "e22fd9eb-3088-4155-936a-0919681c3eb5",
"iss": "https://login.microsoftonline.com/45591230-6e37-4be7-acfb-4c9e23b261ea/v2.0",
"iat": 1634550153,
"nbf": 1634550153,
"exp": 1634636853,
"aio": "E2ZgYGguYd9fNkv3pOV5Iduv2655AgA=",
"azp": "7dd894ca-6c1b-45ae-b67c-75db99593a14",
"azpacr": "2",
"oid": "54e5c672-872f-4866-b067-132973cb0c91",
"rh": "0.ARAAYH9ZRTdu50us-0yeI7Jh6sqU2H0bbK5Ftnx125lZOhQQAAA.",
"sub": "54e5c672-872f-4866-b067-132973cb0c91",
"tid": "45597f60-6e37-4be7-acfb-4c9e23b261ea",
"uti": "qOLzTFlmw0yuWeFXXT1pAA",
"ver": "2.0"
}
Thanks for helping.
The token you might be getting should be an access token and the roles you are looking for should be in the id token. Did you tried this by enabling "ID Token" in azure portal?

Authenticating to Sharepoint Online Site via React SPA in MS Teams personal Tab

When adding a Sharepoint Online site to a MS Teams static Tab referring to this Q&A helps greatly, but how would I go about when having a React single page application Tab and and a Sharepoint online Tab in the same application?
{
"entityId": "id1",
"name": "name1",
"contentUrl": "https://*********.sharepoint.com/_layouts/15/teamslogon.aspx?SPFX=true&dest=/SitePages/Home.aspx",
"scopes": [
"personal"
]
},
{
"entityId": "id2",
"name": "name2",
"contentUrl": "https://{{HOSTNAME}}/tab",
"scopes": [
"personal"
]
}],
As I need to set the defined source to the AAD Application needed for the React SPA I have trouble authenticating to and displaying the Sharepointsite in the MS Teams desktop application.
"webApplicationInfo": {
"id": "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxx",
"resource": "api://subdomain.domain.com/xxxxxx-xxxx-xxxx-xxxx-xxxxxxxx"},
Am I missing something obvious? Would I need to authenticate to Sharepoint trough the AAD App and if so, using which authentication method?
Your help is greatly appreciated!
#AdoTbo, Your webapplicationinfo look good, just make sure correct values are passed. You need to perform AD authentication only. SharePoint authentication also internally uses AD authentication.
You can exchange the AAD token with SharePoint which is getting generated for app resource. Please check the detailed documentation.

App Roles are not delivered in access token

In an Azure Active Directory, I register an App and define application roles in the manifest as follows:
"appRoles": [
{
"allowedMemberTypes": [
"User"
],
"description": "Normal user access",
"displayName": "User",
"id": "a046aca0-a7c4-4c33-8377-e43c4d7bd89f",
"isEnabled": true,
"value": "User"
},
{
"allowedMemberTypes": [
"User"
],
"description": "Full admin access",
"displayName": "Admin",
"id": "0204f02c-d752-48b5-8b8f-2f8ccb7f83d7",
"isEnabled": true,
"value": "Admin"
}
],
"oauth2AllowIdTokenImplicitFlow": true,
"oauth2AllowImplicitFlow": true,
Whenever a user receives an access token, the roles are also included. Everything works fine.
Now we have created another Azure Active Directory. I registered an app and define app roles, exactly with same procedure as before. However the roles are not included in the access token. Could someone please help me to cope with this problem?
First please check if you have assigned the user to roles, you can refer to this tutorial.
Second, I'm not sure if you mean access token or id token. As far as I know, the role will not show in access token. It will just show in id token. You can use flow like authorization code grant or openID connect to sign a user in. The response will have a id_token.
Here is another post which is similar to this problem for your reference.
By the way, if we use "client_credentials" as grant type, we can see the permission roles(in appliction permissions) which added in API permissions(the access token contains a roles property). But if we use "password" grant type, the access token will not have this roles property. So please pay attention to the grant type which you used.
Hope it helps.

How to use ARM templates to deploy a roleAssignment for an App Registration Service Principal?

In my current project I'm working with pre-created App Registration Service Principals in Azure AD. I'm using an ARM template to create a StorageV2 account plus some blob containers, then create a roleAssignment giving Storage Blob Contributor rights to one of the Service Principals. The relevant section of the ARM template is at the end of this post.
What I've found is that if I take the ObjectId of a regular AD user, such as myself or my colleague, and set that as PrincipalId, the script runs correctly. However, I can't get this to work with a Service Principal.
If I use the Service Principal's ObjectId, then I get the following error:
Deployment failed. Correlation ID: 40e0c146-165a-47c0-b022-ac04781d8194. {
"error": {
"code": "PrincipalTypeNotSupported",
"message": "Principals of type Application cannot validly be used in role assignments."
}
}
Having spotted some suggestions for Azure Powershell users that I should use Application (Client) Id instead, I tried that, but get the following error (Guids redacted):
Deployment failed. Correlation ID: 5c725a51-230a-4d85-bb61-b2f4cdf849ff. {
"error": {
"code": "PrincipalNotFound",
"message": "Principal 9f****30 does not exist in the directory db****75."
}
}
So the ObjectId it can find but not use, and the ClientId it can't find.
I have found that if I use Azure Powershell and use the New-AzureRmRoleAssignment command, I can reproduce the PrincipalTypeNotSupported error by providing the Service Principal's ObjectId to the -ObjectId switch. However, that command also has a -ServicePrincipalName switch as an alternative, and if I give that the Service Principal's ClientId, it works!
Is there any equivalent of -ServicePrincipalName for the ARM templates, and if not, is there any other way to achieve this? I can use Azure Powershell as a workaround, but it's messier than I'd like.
If this is a feature gap, where's the best place to report it?
Relevant section of ARM template follows:
"resources": [
{
"name": "[variables('storageAccountName')]",
"type": "Microsoft.Storage/storageAccounts",
"location": "[resourceGroup().location]",
"apiVersion": "2018-07-01",
"sku": {
"name": "[parameters('storageAccountSku')]"
},
"dependsOn": [],
"tags": {
"displayName": "Storage Account"
},
"kind": "StorageV2",
"properties": {
"accessTier": "Hot",
"supportsHttpsTrafficOnly": true,
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Deny"
}
},
"resources": [
{
"type": "blobServices/containers",
"name": "[concat('default/', variables('myBlobContainerName'))]",
"apiVersion": "2018-07-01",
"dependsOn": [
"[variables('storageAccountName')]"
],
"resources": [
{
"type": "Microsoft.Authorization/roleAssignments",
"name": "[variables('myRoleAssignmentGuid')]",
"apiVersion": "2018-07-01",
"properties": {
"roleDefinitionId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe')]",
"principalId": "[variables('myPrincipalId')]"
},
"dependsOn": [
"[concat('Microsoft.Storage/storageAccounts/', variables('storageAccountName'), '/blobServices/default/containers/', variables('myBlobContainerName'))]"
]
}
]
}
]
}
]
Finally solved this one thanks in part to the pointers from #4c74356b41.
When an Application Registration object is created, an identically named object is also created under Enterprise Applications. This has the same ApplicationId, but a different ObjectId, and it's the ObjectId of this Enterprise Application object that our ARM script needs.
You can find this object in the portal by going to the Application Registration entry, then clicking on the link after Managed application in...
Screenshot of App Registration with Link
Once you're on the corresponding Enterprise Application object, you can get the ObjectId from Properties, and use this the value for principalId in the ARM template.
At the time of writing, the Microsoft Documentation is a bit vague on this, with the terms Application and Service Principal seemingly overloaded. This article says that when you register an application you get an Application object and a Service Principal object, but doesn't use the phrase Enterprise Application once, or refer to App Registration objects per se, so it's unclear which is which.
I'm assuming Application == Application Registration and Service Principal == Enterprise Application. This SO post would seem to suggest this is the case, as would the solution above.

Resources