I am trying to find a way how to programmatically change properties of Enterprise Application (non-catalogue app), specifically Secret Token and Tenant ULR in 'Provisioning' blade, allowing to synchronize user/group objects between AAD and an external app (e.g. SaaS app) that supports it.
My customer has a strict policy to rotate all secrets and keys in Azure in regular intervals, so they want to be able to have an automation runbook that would change that token in the app (it is actually an Azure Databricks instance that supports this sync) as well as in AAD.
I checked whether there was a direct PowerShell support but I couldn’t find a specific cmdlet for this scenario (tried both GA and preview versions of AAD PowerShell 2.0).
I found a good documentation page describing AAD Synchronization API - https://learn.microsoft.com/en-us/graph/api/resources/synchronization-overview?view=graph-rest-beta – however, I am unable to find, how to update the Secret Token property.
Ideally, I would like to see a code sample of a REST call on how change that specific property using Synchronization API. A PowerShell example would be even better. Any help is much appreciated. Thanks.
Here's how to do it for non-gallery SCIM apps:
PUT https://graph.microsoft.com/beta/servicePrincipals/99abefe8-3ad8-488f-b14f-df209cbc1ab3/synchronization/secrets
{
value: [
{ key: "BaseAddress", value: "xxxxxxxxxxxxxxxxxxxxx" },
{ key: "SecretToken", value: "xxxxxxxxxxxxxxxxxxxxx" }
]
}
Replace the GUID after servicePrincipals with your real servicePrincipal object ID.
For apps that aren't non-gallery SCIM apps, the credential names required can be discovered in the metadata -> configurationFields part of the synchronizationTemplate object:
https://learn.microsoft.com/en-us/graph/api/resources/synchronization-synchronizationtemplate?view=graph-rest-beta
Related
I have set up a B2C instance OK and managed to get a basic Blazor (server) app working with it a using the Microsoft Identity Platform (using AD groups for permissions - it was a hassle but works).
However, I'm trying to use an external Azure AD as a custom identity provider in the user flow, so that I am not just restricted to just email/id/social accounts, but can have guest accounts from other directories use the app without having to manage their sign-in's. To do that I performed a web app registration in the AD tenant that I wanted to use to authenticate those accounts against (as suggested in a couple of tutorials).
The application I registered in the external AD has a Redirect URI in the format "https://{My B2C Directory Name}.b2clogin.com/{My B2C Directory Name}.onmicrosoft.com/oauth2/authresp", which matches the name of my B2C instance, and I have added the client id and secret generated from that app registration and put the details into the custom identity provider I have created for the sign-in flow, as per the instructions here (including the mappings etc.):
https://learn.microsoft.com/en-us/azure/active-directory-b2c/identity-provider-azure-ad-single-tenant?pivots=b2c-user-flow
I also found a slightly older tutorial here, which is pretty similar (different mappings) that I've tried to follow (and adapt the bits that are out-of-date).
https://medium.com/the-new-control-plane/connecting-azure-ad-b2c-to-azure-ad-via-the-b2c-custom-identity-provider-42fbc2832e32
However when I run the user flow I get "AADSTS900971: No reply address provided." - this happens even when I run the flow directly from the User Flows tab in B2C with a 'Reply URL' explicitly set to "http://jwt.ms" (just to capture the token contents).
I'm confused about the reply URL being missing because they exist in both registered apps. Also, it's not saying they're mismatched, just that one isn't set at all (but appears to be).
It feels like I'm missing something simple - does anyone have any idea what that might be?
Ok so I did a couple of things to resolve this:
Re-registered the application in the AD I want to authenticate with (following this tutorial again: https://learn.microsoft.com/en-us/azure/active-directory-b2c/identity-provider-azure-ad-single-tenant?pivots=b2c-user-flow)
I was careful to ensure that the redirect URI in the format:
https://{B2C Instance Name}.b2clogin.com/{B2C Instance Name}.onmicrosoft.com/oauth2/authresp
was all lower case.
I also had to change from just a 'sign-in' user flow to the 'sign-up, sign-in' one, and then applied the custom identity provider to that flow. Apparently you need that even for users from another AD to be able to complete their invite process (otherwise you just end up with a user doesn't exist error - even if you've invited/added them to the B2C users list).
I also elected to 'Grant admin consent for Default directory' under the API Permissions tab for the application being registered in the external AD (to be used for the custom identity provider).
The flow seems to work now. The only thing that would be useful would be to have an invite only sign-up, sign-in flow so that you could invite specific people without breaking the invite process.
If anyone knows how to do that please do post something.
I'm looking for a way to ensure that the unique_name claim value is always the user email for a specific app in AAD which is accessed via SSO. I've noticed that for users which have an AAD account, the unique_name claim value is the email address, but for external users (invited to the directory) it is prefixed by "live.com#" (e.g. live.com#email#domain.com). The application uses the OIDC protocol. I've been trying different things from Microsoft docs:
Setting a different source for the unique_name claim
https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-saml-claims-customization
(I'm not sure if this applies, given the app uses OIDC, not SAML)
(By the way, that documentation is a bit out of date because I found that the "User Attributes & Claims" section is now in SSO https://learn.microsoft.com/en-us/answers/questions/248748/cant39-find-the-34user-attributes-amp-claims34-sec.html)
Building a claim-mapping policy
https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-claims-mapping
None of these worked.
I know this would be trivial in AAD B2C with custom policies. Is it possible to override the unique_name claim with the email claim in AAD?
Update 07/01/2022
After further investigation I found that the unique_name claim is only supported in v1 tokens https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens, https://github.com/MicrosoftDocs/azure-docs/issues/62971 (the current version is v2) and it's transformed into "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" by the ADAL client. This customization I'm after is for a legacy .NET app which uses ADAL on the v1 AAD endpoint.
The only way I can think of how to achieve this is via a claim-mapping policy, however I haven't been able to do make it work. Any ideas would be appreciated.
I believe I've found an answer to my question and the conclusion is that it's not possible to override the unique_name claim, because it's a restricted claim. The closest I got was to map the mail claim to a custom claim (e.g. unique_name2). This is what I did:
https://learn.microsoft.com/en-gb/azure/active-directory/develop/active-directory-claims-mapping
I installed the Azure AD PowerShell Module public preview (I had to uninstall the AD Powershell module first because I already had a later version)
I followed the instructions on the document above to connect to AAD and view any existing claim mapping policies
Connect-AzureAD -Confirm
Get-AzureADPolicy
I followed the instructions on https://learn.microsoft.com/en-us/answers/questions/80295/claim-transformation-in-azure-id-token-upn-data-to.html to build a claim mapping policy, although I had to adapt it because it wasn't working. I ran the following commands:
3.1 I used the search command to search for my app (called "test").
GET-AzureADServicePrincipal -SearchString Test
This outputs a table like the following:
ObjectId AppId DisplayName
3.2 I ran the following command to create a new mapping policy which maps the "mail" claim to the "unique_name2" claim. (When I tried mapping to "unique_name" it works, but the "unique_name" claim doesn't get overridden, because it's a restricted claim)
$Policy=New-AzureADPolicy -Definition
#('{"ClaimsMappingPolicy":{"Version":1,"IncludeBasicClaimSet":"true",
"ClaimsSchema":[{"Source":"user","ID":"mail"},{"Source":"transformation","ID":"DataJoin","TransformationId":"JoinTheData","JwtClaimType":"unique_name2"}],"ClaimsTransformations":[{"ID":"JoinTheData","TransformationMethod":"Join","InputClaims":[{"ClaimTypeReferenceId":"mail","TransformationClaimType":"string1"}],
"InputParameters":
[{"ID":"string2","Value":""},{"ID":"separator","Value":""}],"OutputClaims":[{"ClaimTypeReferenceId":"DataJoin","TransformationClaimType":"outputClaim"}]}]}}')
-DisplayName "MapMailToUniqueName2" -Type "ClaimsMappingPolicy"
3.3 I ran the following command to add the custom policy to my app service principal (copying the id from step 3.1). This means the policy will be applied only to that specific app.
Add-AzureADServicePrincipalPolicy -Id {my app service principal id}
-RefObjectId $Policy.Id
That's all. When I tested SSO on my app, I had an additional claim "unique_name2" which contains the value of the "mail" claim.
There probably is a more efficient way of creating the mapping policy in step 3.2 but the way described in the step 3 link wasn't working for me so I came up with a concatenation to a blank string.
It doesn't seem possible to override the unique_naim claim because it's a restricted claim. I later found that this is actually documented here https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/active-directory/develop/reference-claims-mapping-policy-type.md. I wish I had found this earlier.
I am using Azure AD with a registered Application and I am using the Microsoft Graph API to query the AD.
The following code below tells which groups the User is Assigned to
var memberof = await graphClient.Users[xxx].MemberOf.Request().GetAsync();
I am using standard AD package and it seems that groups are somewhat restricted and I need to buy the "Premium AD Package" to use them fully.
So I don't want to use the group information. I am interested in the roles that I assign my users that I have put into my application manifest.
e.g
"appRoles": [
{
"allowedMemberTypes": [
"User"
],
"displayName": "Case Manager",
"id": "{A_Guid}",
"isEnabled": true,
"description": "Case Manager's can create and assign Cases to other users",
"value": "CaseManager"
},
So, how can I use the Graph Api to tell me if a user has a particular role ?
1. Microsoft Graph API
The ability to read all application specific roles assigned to a user (i.e. AppRoleAssignments) is only available as part of Microsoft Graph API beta endpoint currently AFAIK. This is not available as part of v1.0. You can read about versions here
As evident from name "beta", it's not expected to be a stable version that can be relied upon for production applications. Read more specific points in this SO Post by Marc LaFleur
Exact API (Microsoft Docs Reference):
GET https://graph.microsoft.com/beta/users/{id | userPrincipalName}/appRoleAssignments
I tried using GraphServiceClient (.NET SDK for Microsoft Graph) but wasn't able to find anything related to AppRoleAssignments. (probably because SDK uses metadata from stable 1.0 version and not the beta version)
In any case, if you can still test this, use Microsoft Graph Explorer or directly call the endpoint from C# code
string graphRequest = $"https://graph.microsoft.com/beta/users/{my user GUID}/appRoleAssignments";
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, graphRequest);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
HttpResponseMessage response = await client.SendAsync(request);
2. Windows Azure AD Graph API
Even though it's recommended to use Microsoft Graph API whenever possible, there are still some cases where Microsoft Graph hasn't caught up yet so you are forced to use Azure AD Graph API. Application management related cases are some of those.
So you could use this Azure AD Graph API. I quickly tested this from Azure AD Graph Explorer and it works fine.
https://graph.windows.net/{yourtenantid}/users/{id}/appRoleAssignments?api-version=1.6
Just like Microsoft Graph Library for .NET you can use Azure AD Graph Client Library for .NET and your code would look something like this..
aadgraphClient.Users["<user guid>"].AppRoleAssignments;
On a side note, since you've asked the question specifically for Microsoft Graph API, I've answered it accordingly.
At least for the currently signed in user for an application, you can always find the Application Roles assigned to them from the Role claims available as part of the access token from Azure Active Directory.
This although only helps with roles for current user and not in management sort of scenarios if you're trying to go across all users for an application. Here's a sample that reads role claims and does authorization based on App Roles for currently signed in user.
Authorization in a web app using Azure AD application roles & role claims
I just found a way to get roles of an user at an application level.
You can create application level roles by updating the manifest's appRoles array.
[azure/app registrations/<your-app>/manifest)]
I used Microsoft.Graph.Beta, to get access to service principals api.
var userRoles = await _client.Me.AppRoleAssignments.Request().GetAsync();
The above query would fetch all the application roles for the user.
var appRoleAssignments = await _Client.ServicePrincipals[<<application_objectId>>].Request().GetAsync();
The above query would fetch all the roles of an application assigned at manifest level.
And application object Id could be found at [azure/app registrations/<your-app>)] -> Object ID
And execute the below to get list of user roles
var roles = new List<string>();
if (appRoleAssignments != null && appRoleAssignments.AppRoles.Any())
{
var userRolesOfCurrentResource = userRoles.First(role => role.ResourceId == Guid.Parse(<<application object id>>));
if(userRolesOfCurrentResource!=null)
{
var role = appRoleAssignments.AppRoles.First(role => role.Id == userRolesOfCurrentResource.AppRoleId);
if (role!=null)
{
roles.Add(role.Value);
}
}
}
I am working with aspnetcore v2.1 (latest dev branches) in order to create a multi-tenant app where each tenant authenticates against their own Azure B2C AD tenant. This aproach was chosen so that email/password selections and social login associations are unique per-tenant.
Instead of a static ClientId applied in Startup.ConfigureServices, I want to apply the correct ClientId and Authority based on the current tenant identity (which I determine based on the hostname). Based on previous inspection of the 2.0-* code, I had been using an IOptionsSnapshot to allow me to apply the correct options as shown below.
In Startup.ConfigureServices:
services.AddSingleton<IOptionsSnapshot<OpenIdConnectOptions>, OpenIdConnectOptionsSnapshot>();
services.AddAuthentication().AddCookie().AddOpenIdConnect();
In Startup.Configure:
app.UseAuthentication();
With an implementation of :
public class OpenIdConnectOptionsSnapshot : IOptionsSnapshot<OpenIdConnectOptions>
However, now I find that my OpenIdConnectOptionsSnapshot is no longer being instantiated or referenced.
What is the correct way to apply a dynamic per-tenant ClientId, Authority, etc under AspNetCore Security 2.1.0-*?
(I am open to "you're doing it completely wrong" and suggestions of different ways to achieve multi-tenancy for tenants that have no pre-existing AzureAD footprint)
Try using IOptionsMonitor instead, we changed how IOptionsSnapshot worked fairly late in 2.0 and switched auth over to use the monitor instead.
OptionsSnapshot is now scoped
A follow up on my earlier question - Are there any APIs available to manage conditional access policies for a tenant?
I'm using the API (https://developer.microsoft.com/en-us/graph/docs/api-reference/beta/api/policy_list) to retrieve the list of policies created for my tenant and have been running into permissions issues.
According to the documentation this API requires a scope of Directory.AccessAsUser.All. On the Azure AD portal, I set the permissions for my test App to include "Read and write directory data" and "Access the directory as a signed-in user". I set these permissions on both Windows Azure Active Directory and Microsoft Graph APIs. When I make a call to https://graph.microsoft.com/beta/policies, I keep getting 403 Forbidden with the error message "Insufficient privileges to complete the operation.". The other operations like getting the users in the directory, getting identityRiskEvents, etc all work fine.
Am I missing anything here?
Thanks in advance!
Actually the answer is no. The GRAPH REST API List Policies does not return conditional access policies yet.
The REST API doc states:
Currently only one type of policy is available:
Token Lifetime Policy - Specifies the lifetime duration of tokens
issued for applications and service principals.
Check the microsoft faq documentation on configuring conditional access.
Are Graph APIs available for configuring conditional access policies?
Currently, no.
FAQ Link
No, There is a REST API List policies of Operations on policy described in Graph API reference, which you can use it to list all policies for your tenant.
Edit: This article lists only Token Policy's are being returned at this moment: https://learn.microsoft.com/nl-nl/graph/api/resources/policy?view=graph-rest-beta
Microsoft have recently released access to conditional access policies in their Graph API beta (https://learn.microsoft.com/en-gb/graph/api/resources/conditionalaccesspolicy?view=graph-rest-beta). The current methods they support for the conditional access policy resource type are:
List
Create
Get
Update
Delete
Similar methods are available for named locations included in a policy.