Grant service principal access to application in other tenant - azure-active-directory

I have an Azure AD service principal in one tenant (OneTenant) that I would like to give access to an application in another tenant (OtherTenant).
The service principal in tenant OneTenant is a managed service identity for an Azure Logic App. So what I actually want is to call an API from my Logic App. This API is protected by an Azure AD application in OtherTenant.
The application in OtherTenant defines a number of roles and the service principal in OneTenant should have one of these roles so it can call the API.
I tried the following:
set the app in OtherTenant to multi-tenant
ran the following PS command to attempt to add the SP to a role in the app:
New-AzureADServiceAppRoleAssignment `
-ObjectId <object-id-of-sp-in-one-tenant> `
-Id <role-id> `
-PrincipalId <object-id-of-sp-in-one-tenant> `
-ResourceId <app-id-in-other-tenant>
(both logged in in OneTenant and OtherTenant)
This gives an error stating that either app-id-in-other-tenant or object-id-of-sp-in-one-tenant can not be found, depending on where I am signed in.
I also tried creating a Service Principal in OneTenant based on the app-id from OtherTenant In that case I get an error message: Authenticating principal does not have permission to instantiate multi-tenantapplications and there is not matching Applicationin the request tenant.

Ok, I finally got around to testing if the solution presented by Rohit Saigal works. It does point in the right direction but is not complete.
First step is to create a service principal in OneTenant that represents the application in OtherTenant. So while signed in to OneTenant, run the following script:
$spInOneTenant = New-AzureADServicePrincipal -AppId <app-id-in-other-tenant>
Next step is to run the New-AzureADServiceAppRoleAssignment cmdlet with the following parameters:
New-AzureADServiceAppRoleAssignment `
-Id <role-id> `
-ObjectId <object-id-of-sp-in-one-tenant> `
-PrincipalId <object-id-of-sp-in-one-tenant> `
-ResourceId $spInOneTenant.ObjectId
The trick is to use the object id of the service principal you created in the previous step as the ResourceId.

Taking the command as is from your question:
New-AzureADServiceAppRoleAssignment `
-ObjectId <object-id-of-sp-in-one-tenant> `
-Id <role-id> `
-PrincipalId <object-id-of-sp-in-one-tenant> `
-ResourceId <app-id-in-other-tenant>
Try changing the last parameter value i.e. ResourceId
Currently you're passing <app-id-in-other-tenant>
Replace that with <object-id-of-API-in-other-tenant>

The question/answers presented here were helpful, but yet it took me some time to work through the details and actually make it work, so allow me to elaborate some more on the above, as it might help others too.
Scenario
Two tenants:
Home Tenant.
Other Tenant.
One Azure App Service API app, with access managed by the Home Tenant.
One Logic app placed in a subscription in Other Tenant that need to securely access the API app in the Home Tenant.
Approach
Application registration
For the API app to delegate identity and access management to Azure AD an application is registered in the home tenant’s Azure Active Directory. The application is registered as a multi-tenant app.
You also need to create an app role (see documentation: How to: Add app roles in your application and receive them in the token), lets call it read.weather.
Configuration of Other Tenant and Logic App
To provide the Logic App access to the API app do this:
Enable a system assigned identity for the logic app - i.e. use Managed Identity. Note down the system assigned managed identity Object ID ({18a…}), you will need it in a minute.
Create a service principal for the application in the Other Tenant using this command, where appId is the appId of the application registered in Home Tenant (e.g. lets say it’s {1a3} here):
New-AzureADServicePrincipal -AppId {appId}
This command will output a json document which includes among other things an objectId for the service principal just created.
[... more json ...]
"objectId": "b08{…}",
[... more json...]
You should also see the appRoles, including the app role read.weather you just created, om the json output from the New-AzureADServicePrincipal command:
[... more json...]
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"description": "Read the weather.",
"displayName": "Read Weather",
"id": "46f{…}",
"isEnabled": true,
"value": "read.weather"
}
],
[... more json...]
To tie it all together make an app role assignment using the command:
New-AzureADServiceAppRoleAssignment -Id {app role ide} -ObjectId {system assigned identity object id} -PrincipalId {system assigned identity object id} -ResourceId {object id of service principal}
E.g. something like this:
New-AzureADServiceAppRoleAssignment -Id 46f… -ObjectId 18a… -PrincipalId 18a… -ResourceId b08…
Now we are ready to set up the HTTP request in the Logic App using the URI to the API app using Managed Identity for authentication.
Notice that the audience is needed and has the form: api://{appId}.
But wait…!
Does this mean that anyone could do this, if only they know the appId of your application registration, and wouldn’t this compromise the security of the API app?
Yes, it does, and yes indeed it could compromise the security of the API app.
If you look at the access token being created and used as bearer from the Logic App it is a valid access token, which contains all the necessary claims, including the app role(s). The access token is however issued by Other Tenant and not Home Tenant.
Therefore, if you have a multi tenanted application and you want to guard it against this scenario, then you could check the issuer (tid more likely) of the incoming access token calling the API and only accept it if the issuer is the Home Tenant, or you could make the application a single tenant app.
Or, you could require that the issuer matches a list of tenants that the API trusts.

Related

Can I add a custom static claim to auth tokens with a AAD OIDC app registration?

I have a basic Azure AD App Registration. This is a non-interactive client IE console/machine client with a secret and no redirect URI. I would also like this to also work for interactive clients however.
How can I add a claim to my token called "hello": "world"?
To add a custom claim like "hello": "world" in the token, you can create claim mapping policy using PowerShell.
1)Make sure to have AzureADPreview module installed, before running below commands.
Connect-AzureAD
New-AzureADPolicy -Definition #('
{
"ClaimsMappingPolicy":
{
"Version":1,"IncludeBasicClaimSet":"true",
"ClaimsSchema": [{"Source":"user","ID":"extensionattribute1","SamlClaimType":"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/hello","JwtClaimType":"hello"}]
}
}') -DisplayName "HelloExtraClaim" -Type "ClaimsMappingPolicy"
Output:
2)Note the ID of the policy and assign it to your service principal using below command:
Add-AzureADServicePrincipalPolicy -Id serviceprincipal_ObjectID -RefObjectId policy_ID
3)To confirm whether the policy is assigned or not, run below command:
Get-AzureADServicePrincipalPolicy -Id serviceprincipal_ObjectID
Output:
4)To assign value to that claim, sign in as admin to Microsoft Graph Explorer and run the below query:
PATCH https://graph.microsoft.com/beta/me
{
"onPremisesExtensionAttributes":
{
"extensionAttribute1": "world"
}
}
Response:
5)Make sure to set "acceptMappedClaims": true in App's Manifest like below:
Go to Azure Portal -> Azure Active Directory -> App registrations -> Your App -> Manifest
6)Go to Expose an API tab and set Application ID URI to your domain like below:
7)Generate a token to your application by signing with admin account.
After decoding the above ID token in jwt.ms, I got the claim "hello": "world" successfully as below:
Credits: How to add custom user defined claims to azure ad token | GitHub by TiagoBrenck

Microsoft OAuth authorization for specific user roles

I have to limit which users can access an Azure App. For now, only Global Admins can access using this link:
login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=client_id_goes_here&scope=user.read.all&response_type=code&redirect_uri=https://myredirectbacklink.com/aad/auth&response_mode=query&state=portal&prompt=consent
After redirect back I get Token using
https://login.microsoftonline.com/common/oauth2/v2.0/token and the query contains the same scope as the authorized link.
The flow of the app is the same as documented in Microsoft identity platform and OAuth 2.0 authorization code flow.
My problem is that client wants to allow Billing Admins to access their app. I went through all Graph Permission Scopes, but could not find any related to Billing Admin.
My knowledge of Microsoft's authorization is somewhat limited. I do know that limitations are done by scope. But if it's just link change or is it in Authentication App in Azure (there is one but I don't know how it's related to actual login permissions).
Should I look in a different direction or is it just simply changing the link? Microsoft's documentation didn't help a lot because it's mostly about calendars and other simple stuff.
Careful, requesting a permission that normally requires admin consent and causing the user consent prompt is not the proper way to ensure the user signing in is actually an admin. A non-admin user could simply modify the URL to scope=User.ReadBasic.All and remove prompt=consent. If the user is allowed to consent for User.ReadBasic.All (which is true in many organizations), they'd be able to continue the sign-in. (Or if the organization had granted admin consent for "User.Read.All", the non-admin user would only need to remove prompt=consent.)
If you need to ensure the user is an administrator, you need to explicitly check for directory role assignments.
You can choose from one of three different ways to do this:
You can configure your app to receive the wids claim, which will include the role template IDs of the directory roles for which the user has an active assignment. This is probably the simplest approach.
Using the Azure portal, you can do this under App registrations > (choose your app) > Token configuration > + Add groups claim. You must include "Directory roles" in your selection:
Another option is to a Microsoft Graph API request to check which of a given list of directory roles the user has been assigned:
POST https://graph.microsoft.com/v1.0/me/checkMemberObjects
Content-type: application/json
{
"ids": [
"fdd7a751-b60b-444a-984c-02652fe8fa1c",
"b0f54661-2d74-4c50-afa3-1ec803f12efe"
]
}
A third option is to make a Microsoft Graph API request to list the directory role assignments granted to the user:
GET https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments
?$filter=principalId eq '{id}'
All three of these approaches involve using directory role template IDs to identify the directory role you are checking for. They're all listed here: https://learn.microsoft.com/azure/active-directory/roles/permissions-reference
Some examples you may be interested in:
Application administrator: 9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3
Cloud application administrator: 158c047a-c907-4556-b7ef-446551a6b5f7
Global administrator: fdd7a751-b60b-444a-984c-02652fe8fa1c
Privileged role administrator: e8611ab8-c189-46e8-94e1-60213ab1f814
Billing administrator: b0f54661-2d74-4c50-afa3-1ec803f12efe
(I included the first four because those are the directory roles which would, by default, be allowed to grant consent for User.Read.All.)
If someone needs solution and uses php:
You can use https://github.com/microsoftgraph/msgraph-sdk-php
$accessToken = 'token from redirect back, called access_token';
$body = [
"ids" => [
"fdd7a751-b60b-444a-984c-02652fe8fa1c",
"b0f54661-2d74-4c50-afa3-1ec803f12efe"
]
];
$graph = new Graph();
$graph->setAccessToken($accessToken);
$user = $graph->createRequest("post", "/me/checkMemberObjects")
->attachBody($body)
->execute();

Reset Password REST call returns 403 using service principal

I am trying to execute the following API using a bearer token issued to a service principal NOT an AD User. Yes I know this is the AD Graph versus Microsoft Graph endpoint, but I have my reasons :)
https://graph.windows.net/GUID-REDACTED/users/GUID-REDACTED?api-version=1.6
I get a 403 error despite the fact that I have granted all Application Permissions for "Windows Azure Active Directory" (and Microsoft Graph) to that principal. I also applied admin consent (via Grant Permissions) in the portal. Note that a request to read all users (i.e., remove last GUID off URL above) DOES succeed.
The bearer token contains the following seven claims (curiously in AD, EIGHT permissions are granted):
"Device.ReadWrite.All",
"Directory.Read.All",
"Member.Read.Hidden",
"Directory.ReadWrite.All",
"Domain.ReadWrite.All",
"Application.ReadWrite.OwnedBy",
"Application.ReadWrite.All"
The token was acquired via:
var context = new AuthenticationContext($"https://login.microsoftonline.com/{tenantId}");
var m = new HttpRequestMessage()
var accessToken = context.AcquireTokenAsync("https://graph.windows.net", credentials).Result.AccessToken;
m.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
I have tried the analog of this via the Microsoft Graph endpoint, but with the ADAL AuthenticationContext and get the same 403 result. If I use the Microsoft Graph Explorer though, it works. In that case I am logged in as a user though. Upon comparing the scopes in the tokens (scp) there are differences (because the user has certain 'user' scopes), but nothing that immediately looks suspicious.
Directory.AccessAsUser.All is on the user scope but not the application identity scope, but that makes sense to me, unless that scope is (incorrectly?) required for the operation I am trying.
Any ideas what I am missing here? Is there a reference that maps the scopes/roles required to the actual API operations? Does the SP need a directory role, like a user would need?
As #MarcLaFleur said, it's not a good idea to give an application permissions to reset users' password. But if you don't have other choices you can still use client credentials flow to achieve this. But this is not recommeded unless you don't have other choices.
Solution:
You can assign Company Administrators Role to your Service principal. You can refer to this document to do that.
Use AAD Powershell to Connect AAD:
Connect-AzureAD
Get the Role of Company Administrator:
$role = Get-AzureADDirectoryRole | Where-Object {$_.displayName -eq 'Company Administrator'}
Assign the role to your SP:
Add-AzureADDirectoryRoleMember -ObjectId $role.ObjectId -RefObjectId $yoursp.ObjectId

Adding users with roles into app registration

I can see you can create app registrations with graph API but is it possible to add users and roles to an app registration?
If you want to assign a user to that application, in one of the specified app roles, you'll need to set the appRoleAssignment on the user . If using Azure AD Graph api , you could use below rest api :
https://graph.windows.net/{tenant-id}/users/{username}/appRoleAssignments?api-version={api-version}
Content-Type:application/json
Authorization: Bearer {token}
{
"id": "RoleID",
"principalId": "UserObjectID",
"principalType": "User",
"resourceId": "servicePrincipalID"
}
id: This is the Id for the Role you are assigning. These Ids can be found in the Application's Manifest. Or you could use below api to get the specific role defined for your application(appRoles claim):
https://graph.windows.net/{tenant}/applications/{ObjectIDOfApplication}?api-version=1.6
principalId :This is the Obeject Id of the User you are assigning the role to.
principalType :If you are assigning this role to a User then this is set to the string User .
resourceId : Service Principal ID of the application . To get service principal id , you could use below api (objectId claim) :
https://graph.windows.net/{tenant}/servicePrincipals?api-version=1.6&$filter=appId eq 'appid'
Application role assignments are available in the Microsoft Graph Beta endpoint: see Update approleassignment
To give you an idea of what could do to add app role assignments to a user, I suggest you look at the Configure.ps1 PowerShell script of the active-directory-dotnet-webapp-roleclaims sample on GitHub, which creates test users of a given role and updates the application registration. This is in PowerShell, but you should be able to adapt it to using MSGraph

How to programatically create applications in Azure AD

I'm currently creating my applications on Azure Active directory manually whenever there is a request for a new environment. I was exploring ways to create these applications from the code via REST API. I had success in creating users and groups on existing applications by using 'client_credentials' as shown.
ClientCredential clientCred = new ClientCredential(clientID, clientSecret);
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenAsync(resAzureGraphAPI, clientCred);
In similar fashion I tried to use the 'access_token' generated from above to create a new application
adClient.Applications.AddApplicationAsync(newApplication).Wait()
But this throws an error- "Insufficient privileges to complete the operation."
I looked at other threads and the Azure AD msdn page and turns out the client_credentials flow does not support creating/updating applications.
Adding Applications programmatically in Azure AD using Client Credentials Flow
The above thread also mentioned that way to workaround it was by using the 'grant_type=password' flow. I tried it as suggested but I keep getting the following error which doesn't make sense to me.
"error": "invalid_grant",
"error_description": "AADSTS50034: To sign into this application the account must be added to the 1283y812-2u3u-u293u91-u293u1 directory.\r\nTrace ID: 66da9cf9-603f-4f4e-817a-cd4774619631\r\nCorrelation ID: 7990c26f-b8ef-4054-9c0b-a346aa7b5035\r\nTimestamp: 2016-02-21 23:36:52Z",
"error_codes": [
50034
],
Here is the payload and the endpoint that I'm hitting. The user that is passed is the owner of the AD where I want to create the application
endpoint:https://login.windows.net/mytenantID/oauth2/token
post data
resource 00000002-0000-0000-c000-000000000000
client_id id
client_secret secret
grant_type password
username principal#mydomain.com
password password
scope openid
Any thoughts or suggestions of where I might be going wrong would be appreciated.
You can use PowerShell to create your apps:
$servicePrincipalName =”Your Client App Name”
$sp = New-MsolServicePrincipal -ServicePrincipalNames $servicePrincipalName -DisplayName $servicePrincipalName -AppPrincipalId “Your Client ID"
New-MsolServicePrincipalCredential -ObjectId $sp.ObjectId -Type Password -Value “Your client secret”
Add-MsolRoleMember -RoleObjectId “62e90394-69f5-4237-9190-012177145e10" -RoleMemberType ServicePrincipal -RoleMemberObjectId $sp.ObjectId
The role denoted by 62e90394-69f5-4237-9190-012177145e10 is the Admin role, and this can be adjusted as required to the ObjectId of any other role. Run Get-MsolRole to get a list of roles and ObjectIds.
You could then run this code from your App or run it manually. You will also need to run your connection code before the above, something along the lines of:
$loginAsUserName = "Your Tenancy Admin Account"
$loginAsPassword = "Your Tenancy Admin Account Password"
$secpasswd = ConvertTo-SecureString $loginAsPassword -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ($loginAsUserName, $secpasswd)
Connect-MsolService -Credential $creds
I was able to create the application in my tenant. The AD tenant which I was using to create the application under was verified for a different domain. Basically I ended up plugging in an user from that domain and using the resource_type=password flow was able to generate an access token. Next, firing the following lines of code did the trick
ActiveDirectoryClient adClient = new ActiveDirectoryClient(
serviceRoot,
AccessToken);
adClient.Applications.AddApplicationAsync(newApplication).Wait();
Check the following things which seem to be a little off in your POST to the OAuth Token endpoint:
When wanting access to the Graph API of your Azure AD, you will need to pass https://graph.windows.net as the resource body parameter; this is (imho) not well documented, but that's what you need to do
As client_id and client_secret you need to pass the Client ID and the Key of a predefined Application inside your Azure AD which in turn you have granted permissions on a per user level to; these need to be sufficient to add applications
See here: https://msdn.microsoft.com/Library/Azure/Ad/Graph/howto/azure-ad-graph-api-permission-scopes?f=255&MSPPError=-2147217396
The scope parameter is not used, I think; you will get the claims you defined inside the Azure AD management portal back (the assigned permissions for your application)
This should render you an access token you can then subsequently use on the https://graph.windows.net/tenantId/ end points.

Resources