Creating / managing Azure Active Directory B2C with a Service Principal - azure-active-directory

I'm in the process of setting up a Bicep-based pipeline to provision and manage the infrastructure for an application, and I am currently running into an obstacle with the AAD B2C tenant. I have sorted out the basics of deploying a B2C tenant from Bicep given the following resource definition (deployed via a module):
resource b2cDirectory 'Microsoft.AzureActiveDirectory/b2cDirectories#2021-04-01' = {
location: country.name
name: tenantName
sku: {
name: 'PremiumP2'
tier: 'A0'
}
properties: {
createTenantProperties: {
countryCode: country.code
displayName: 'B2C - ${tenantDisplayName}'
}
}
}
I can provision this resource just fine while logged into the az cli with an AAD user account that's granted Owner or Contributor in the target subscription, however, this creates a small hitch for further automation due to the B2C tenant management model (requiring B2C identities for management rather than existing organization identities). When the Azure Resource Manager provisions B2C, it adds the creator as a Global Administrator of the new tenant. Further management tasks, e.g., app registrations and custom policy deployment, require the user to be logged in to the B2C tenant.
In order to automate the end-to-end process, especially within a pipeline, I would like to use a service principal to provision B2C so that my deployment scripts can continue to log in to the new tenant and perform additional management tasks. However, when attempting this, I encounter an error while creating the new tenant:
{
"status": "Failed",
"error": {
"code": "DeploymentFailed",
"message": "At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-deployment-operations for usage details.",
"details": [
{
"code": "Conflict",
"message": "{\r\n \"status\": \"Failed\",\r\n \"error\": {\r\n \"code\": \"ResourceDeploymentFailure\",\r\n \"message\": \"The resource operation completed with terminal provisioning state 'Failed'.\",\r\n \"details\": [\r\n {\r\n \"code\": \"DeploymentFailed\",\r\n \"message\": \"At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/arm-deployment-operations for usage details.\",\r\n \"details\": [\r\n {\r\n \"code\": \"Unauthorized\",\r\n \"message\": \"{\\r\\n \\\"error\\\": {\\r\\n \\\"code\\\": \\\"Unauthenticated\\\",\\r\\n \\\"message\\\": \\\"Unauthenticated\\\",\\r\\n \\\"details\\\": null,\\r\\n \\\"target\\\": \\\"Authentication\\\",\\r\\n \\\"additionalInfo\\\": null\\r\\n }\\r\\n}\"\r\n }\r\n ]\r\n }\r\n ]\r\n }\r\n}"
}
]
}
}
My best guess at this point is that service principals are not supported for this case and that AAD does not know how to bootstrap the Global Administrator for the new tenant. Has anyone overcome this with a service principal? If not, I'd appreciate other suggestions.
If I can't automate it completely, the fallback approach will likely be to write a local script to perform the initial B2C provisioning and manually bootstrap an internal service principal that will be accessible from a devops pipeline and Bicep deployment scripts. This isn't ideal, but it will work for now.

Related

Create Microsoft teams meeting through API

In Azure Active Directory I've registered a new app and given it the Read and create online meetings permissions. I've granted admin consent for the permission and now I'm trying to create a meeting through the command line.
I generated a client secret for the app.
Then I'm requesting a access token using my tenant GUID, client ID of the app and client secret I generated. This gives me back a jwt. When I decode the JWT amongst the roles I can see "OnlineMeetings.ReadWrite.All" which gives me hope that I can actually create meetings using this bearer token.
I then send a POST request to https://graph.microsoft.com/v1.0/users/<my-user-guid>/onlineMeetings
with the following body:
{
"startDateTime":"2021-03-16T14:33:30.8546353-07:00",
"endDateTime":"2021-03-16T15:03:30.8566356-07:00",
"subject":"Application Token Meeting",
"participants": {
"organizer": {
"identity": {
"user": {
"id": "<my-user-guid>"
}
}
}
}
}
and the response comes back with
"code": "Forbidden",
"message": "Application does not have permission to Create online meeting on behalf of this user.",
Am I missing something?
Edit:
As some of the comments have suggested I should create an application access policy. So I'm following the documentation which asks me to Connect using admin credentials
When i run Connect-MicrosoftTeams -Credential $userCredential with my account it fails with the following error:
Connect-MicrosoftTeams: accessing_ws_metadata_exchange_failed: Accessing WS metadata exchange failed: Response status code does not indicate success: 406 (NotAcceptable).
Connect-MicrosoftTeams: accessing_ws_metadata_exchange_failed: Accessing WS metadata exchange failed
Connect-MicrosoftTeams: Response status code does not indicate success: 406 (NotAcceptable).
Connect-MicrosoftTeams: : Unknown error
But this is what is confusing me. I don't really know if I'm trying to login with the correct account. I'm using my personal account on azure which is (afaik) not a business account with skype for business.
I'm running the commands to log in on the azure portal's PowerShell interface. Am I supposed to run this on my local machine instead?
I think I'm not fully understanding what all of the moving parts are that need configuration.
Can I add those application Access Policies in the azure portal interface somewhere?
According to the api documentation, make sure you grant the OnlineMeetings.ReadWrite.All application permission to the application. Then you need to use the client credential flow to obtain an access token.
Please note that when you create an online meeting with an application token, administrators must create an application access policy and grant it to a user, authorizing the app configured in the policy to create an online meeting on behalf of that user (user ID specified in the request path).

Tenant 'xxx' not found while calling MS Graph API on behalf of a user

I'm trying to implement the on behalf flow with Azure AD following this Microsoft sample documentation, all is good on the client-side but in the
the service app side (the azure function that obtains another Access Token using the on user's behalf and calls the MS Graph API on user's behalf again), it fails to obtain the access a new access token (getNewAccessToken) and shows:
{
"error": "invalid_request",
"error_description": "AADSTS90002: Tenant 'xyz' not found. This may happen if there are no active subscriptions for the tenant. Check to make sure you have the correct tenant ID. Check with your subscription administrator.\r\nTrace ID: xxx \r\nCorrelation ID: yyy \r\nTimestamp: 2021-01-04 07:17:15Z",
"error_codes": [
90002
],
"timestamp": "2021-01-04 07:17:15Z",
"trace_id": "xxx",
"correlation_id": "yyy",
"error_uri": "https://login.microsoftonline.com/error?code=90002"
}
any clue how to solve this issue?
Based off your error message, you can navigate to your Azure Active Directory and make sure your TenantID matches what's in your application.
Error Message:
AADSTS90002: Tenant 'xyz' not found. This may happen if there are no active subscriptions for the tenant. Check to make sure you have the correct tenant ID.
TenantID

To-Do API: The Service is not available

When calling the MS Graph API on the endpoint https://graph.microsoft.com/beta/me/todo/lists, I get the following result (same when using v1.0 instead of beta):
{
"error": {
"code": "UnknownError",
"message": "The service is unavailable.",
"innerError": {
"date": "2021-01-05T18:36:43",
"request-id": "a4549f79-399a-401b-84eb-cc2f8f6197c8",
"client-request-id": "e07b23bb-60bd-be9b-39db-60953ff42844"
}
}
}
Permission Tasks.ReadWrite is consented and the authenticated user account is a personal account (aka Microsoft Account). With a "Work or school account", the API works as expected.
As of the documentation, this endpoint should be fully available in v1.0 and beta versions but it obviously is not - or I do something wrong. Any suggestions?
You can try to use graph-explorer to log in to your personal Microsoft account and call the api. I just tested it with this tool and it did work for me. (Don't forget to add Tasks.ReadWrite permission and consent)
Another method is to add your personal Microsoft account as a guest user to the Azure tenant (note: guest users need administrator role to call the api), then grant Tasks.ReadWrite delegation permissions to the application, and then use the auth code flow Obtain an access token. This requires you to log in to your personal Microsoft account to obtain an authorization code, and then use the authorization code to redeem an access token.

Azure AAD and Graph API: Insufficient privileges to complete the operation

Context: I've a console app which wants to use Graph API to talk to AAD to check if a particular userId exists in the tenant or not.
I've been following the guidelines here: https://learn.microsoft.com/en-us/graph/auth-v2-service?view=graph-rest-1.0
I'm able to generate a token using this:
https://login.microsoftonline.com/common/oauth2/v2.0/token
client_id=x
&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
&client_secret=x
&grant_type=client_credentials
But when I call the graph API I get this ERROR:
https://graph.microsoft.com/v1.0/users/12345678-73a6-4952-a53a-e9916737ff7f
{
"error": {
"code": "Authorization_RequestDenied",
"message": "Insufficient privileges to complete the operation.",
"innerError": {
"request-id": "x",
"date": "x"
}
}
}
My AAD App has all the permissions from:
1. Microsoft Graph
2. Windows Azure Active Directory
I tried changing the scope to
scope=https%3A%2F%2Fgraph.microsoft.com%2Fuser.read
But this is the error I get while generating token:
The provided value for the input parameter 'scope' is not valid. The scope https://graph.microsoft.com/user.read is not valid.
I've tried combinations of "User.Read", "User.Basic.Read", etc. but nothing works.
The most likely reason why this is not working is because the permission which you have configured your app registration to require have not actually been granted by an administrator of your organization.
In your code, your app is authenticating as an application only. There is no signed-in user involved, and it requires your app to use and keep confidential a key used to authenticate (the client_secret parameter).
In this scenario, requesting the scope https://graph.microsoft.com/.default is the correct approach. What you're saying to Azure AD is: "please provide an access token for all the application permissions this app has been granted". Requesting the scope https://graph.microsoft.com/User.Read is not the correct approach because there is no application permission with that name.
Does the app you created have delegated permissions or application permissions to that scope?
Most likely the former. Delegated permissions don’t apply to client credentials flow.

Azure AD / Microsoft Graph Access Level Upgrade and Downgrade

Is there a way to upgrade and downgrade the Azure AD / Microsoft Graph access level?
For example, can a user signup with a web app only to login and later upgrade access to One Drive or downgrade back to login? I was looking for a way to unauthorize the user access and then reauthorize with the different set of permissions but couldn't find a way to unauthorize.
You can get incremental consent if you use the V2 endpoint: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-appmodel-v2-overview.
There you can specify which scopes you want to require when redirecting the user to login.
So you can require basic ones at the start, and then if they want to enable additional features/you release an update which requires additional permissions, you can add those quite easily.
As for the other direction, no.
Once a user has given consent for some permissions, the only way to undo the consent would be to delete the oauth2PermissionGrant object mapping the user to the application's service principal.
In case of an application permission, an appRoleAssignment would need to be deleted from the service principal.
So it's possible, but you will have to call Microsoft Graph API yourself.
You can get all permission grants via: https://graph.microsoft.com/beta/oauth2PermissionGrants and it returns grant objects like this:
{
"clientId": "e846195b-9b20-4001-ad84-5ab5de5531e6",
"consentType": "AllPrincipals",
"expiryTime": "2018-05-04T09:39:32.9697945Z",
"id": "WxlG6CCbAUCthFq13lUx5s7PF6398j5LkfWqCoLpQBI",
"principalId": null,
"resourceId": "ad17cfce-f2fd-4b3e-91f5-aa0a82e94012",
"scope": "User.Read Directory.AccessAsUser.All",
"startTime": "0001-01-01T00:00:00Z"
}
This one is actually the result of admin consent (consentType = AllPrincipals and principalId = null).
For regular user consent, principalId will be the id of the user.
clientId is the id of the service principal who was granted access, and resourceId is the target service principal.
You can also filter the results to a specific user for example: https://graph.microsoft.com/beta/oauth2PermissionGrants?$filter=principalId eq '73c38a25-23eb-44eb-bf63-4aa987b2ef19'
You can then update the grant to change the approved scopes by running a PATCH to https://graph.microsoft.com/beta/oauth2PermissionGrants/WxlG6CCbAUCthFq13lUx5s7PF6398j5LkfWqCoLpQBI with a body like:
{
"scope": "User.Read"
}
You can delete the grant entirely by running a DELETE on the same URL.

Resources