My AzureAD - registered application doesn't have the rights to do anything ...? - c#

I'm writing a c# program right now that tries to authenticate with Azure to make a generic http request. I finally got the code working and I wanted to test the features but for every request I make I get the following error code in response:
{"error":{"code": "AuthorizationFailed", "message":"The client "(id of the app I registered in AzureAD)" with object id "(same value as before)" does not have authorization to perform action 'Microsoft.Authorization/roleAssignments/read' over scope '/subscriptions/(mysubscriptionid)'."}}.
The thing is ... The account I use to set everything up is a global admin. I checked every permission box in AzureAD I could find...
(that's 8 Application Permissions and 9 Delegated permissions in the Windows Azure Active Directory API and 1 delegated Permission in the Windows Azure Service Management API, though I don't know why there aren't more possible permissions for Azure Service Management)
the relevant code is rather basic but it works so I don't feel like I need post it, I'll just say that I obtain the Token using Microsoft.IdentityModel.Clients.ActiveDirectory.AcquireTokenAsync() with
authorityUri = "https://login.windows.net/(mytenantid)",
string resourceUri = "https://management.azure.com/";
AuthenticationContext authContext = new AuthenticationContext(authorityUri);
var res = authContext.AcquireTokenAsync(resourceUri, new
ClientCredential(clientId,clientSecret));
return res.Result;
and make the Request to
https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Authorization/roleAssignments?api-version=2016-03-01&$filter=atScope()
(as an example, this one is supposed to call the roles).
Tried several different types of GET Requests to different URIs, all give similar errors.
I'm thinking it might not be an issue with the code but a setting in the AzurePortal but everything I found there seems set up right (or rather "universal access").

According to your description, you forget to grant your service principal. You could add it on Azure Portal. You could grant Contributor or Owner role to it.
Please refer to this link:Assign application to role.

Related

How do I keep/store my azure credentials?

I have this web app which access a keyvault stored in Azure cloud.
To access this KeyVault I use the IConfigurationBuilder Extension
configuration.AddAzureKeyVault(new Uri(KeyvaultUri), new DefaultAzureCredential(true));
I have created an managed identity for all the user who need access to this, meaning they should be able to run the application and have access to the keyvault once they are logged in via SSO, which they currently are forced to do everytime they start the application due to new DefaultAzureCredential(true) What I don't understand is why it everytime need to be requested everytime, and not store the credentials somewhere after it has been entered once, and use that stored credential, can I somehow locally store the required credentials after the initial login?
It is sort of inconvenient to always login when one start their application, and debugging application becomes a bit lengthy with the required login.
Is somehow possible to let the login happen in the background - or somehow store the credentials after first login?
I feel a bit this is getting off tracked - the solution I am seeking should be applicable for those running the solution via a terminal, outside of visual studio.
Such as frontend developers - who just need a backend to make reqeuest to a nothing else.
It has no sense to cache the token since it is used only once at startup, what you are looking for is to exclude in your credentials all the wrong ways you are trying to grab the token to connect to your AKV except the one you are really using.
To configure it correcly and do not wait 15 seconds in your startup you should configure DefaultAzureCredentials this way:
DefaultAzureCredential credentials = new DefaultAzureCredential(new DefaultAzureCredentialOptions
{
ExcludeEnvironmentCredential = true,
ExcludeInteractiveBrowserCredential = true,
ExcludeAzurePowerShellCredential = true,
ExcludeSharedTokenCacheCredential = true,
ExcludeVisualStudioCodeCredential = true,
ExcludeVisualStudioCredential = true,
ExcludeAzureCliCredential = true,
ExcludeManagedIdentityCredential = false,
});
Exclude all posibilities to grab the token except the one you are using, in this case "Managed Identity" or in other cases AzureCliCredentials.
Regards.
Can you share a small full code example ?
What about using DefaultAzureCredentialOptions.
Like:
.ConfigureAppConfiguration((context, config) =>
{
var appSettings = config.Build();
var credentialOptions = new DefaultAzureCredentialOptions();
var credential = new DefaultAzureCredential(credentialOptions);
config.AddAzureKeyVault(new Uri(appSettings["Url:KeyVault"]), credential);
})
I assume you've got different Azure subs for non-prod and prod (that contain a non-prod and prod key vault instances).
In the non-prod key vault instance, create a new role assignment with the required members i.e. the developer AAD accounts.
Given you're using DefaultAzureCredential, these members will be able to leverage Azure service authentication in Visual Studio or Visual Studio Code (or EnvironmentCredential) as DefaultAzureCredential will cycle through these various credential types including VisualStudioCredential and VisualStudioCodeCredential (and EnvironmentCredential) respectively.
Developers can authenticate in their IDE e.g. in Visual Studio by going to Tools > Options > Azure Service Authentication where they can authenticate using their Azure credentials.
Assuming their AAD accounts have been granted access to the (non-prod) key vault instance, they will be able to get access.
The deployed application - presumably running in Azure - would use a different credential type e.g. ManagedIdentityCredential or EnvironmentCredential. Given these are also handled by the DefaultAzureCredential, no code changes would be required for this to work for the deployed instance of your app.
The only difference with the prod key vault instance is that you probably wouldn't create role assignments for the developer accounts.
the solution I am seeking should be applicable for those running the solution via a terminal, outside of visual studio. Such as frontend developers - who just need a backend to make reqeuest to a nothing else.
For these type of users that just want to run the app from the terminal, they could set some environment variables that will get picked up by the EnvironmentCredential (which is another one of the credential types included in the DefaultAzureCredential) e.g. if they're running the app in docker they could specify AZURE_USERNAME and AZURE_PASSWORD environment vars (or alternatively AZURE_CLIENT_ID and AZURE_CLIENT_SECRET for more of a 'machine' context) e.g. docker run -e AZURE_USERNAME=username -e AZURE_PASSWORD=password ...
Based upon your question, I am assuming the following:
The web application has a managed identity and has
permissions to your key vault.
The users are logging in as themselves
in the front end of the web application.
The new DefaultAzureCredential(true) just grabs the users current credentials. This will be the front end user. These are cached automatically based upon your organization's security policy. I assume this is working correctly.
The login frequency is out of your control as a developer. The issue you are having is in the organizational settings in Azure Active Directory. Your sign in frequency may be set to one of these:
Require reauthentication every time
Sign-in frequency control every time risky user
To fix your issue, set the sign-in frequency to be less than that and you should be good. (I don't have access to this or I would post better pictures)
Here is the link to the full article on how to do this:
Configure authentication session management with Conditional Access
This was what I was looking for
https://github.com/Azure/azure-sdk-for-net/issues/23896
A silent authentication step that stores the credentials first time it is entered into a cache file and then reuses it when rerunning the application the second time without prompting the user to enter credentials.
Thus ensuring credentials arent stored in git, and cache file stored locally on each developers local machine.

AAD client-credentials flow request scope for web api

I got a console app that needs to support two flows, authenticating against AAD to talk to a web api:
for regular usage by humans, it needs to support interactive login
for usage by a CI/CD pipeline it needs to support client-credentials.
The interactive flow works perfectly, but the client-credentials flow is giving me problems with the requested scope.
I'm using the latest Microsoft.Identity* nuget packages.
When I construct the scope for the webapi like I do for the interactive flow, I get an error message telling me that for client-credentials flows I need to append ./default. Okay, fair enough, I also found documentation for this, so I append ./default. But when I do that, I get another error message telling me
The resource principal named api:///access_as_user was not found in the tenant named .
There are two problems with this error message:
the resource principal quoted definitely exists - also, I couldn't login interactively if it didn't, but as mentioned, interactive login works just fine
it says api://<webApiAppId>/access_as_user, not api://<webApiAppId>/access_as_user/.default, despite my appending that
My next though was: well, maybe the problem is that the app registration used for the client-credentials flow doesn't have permissions on the web-api. But it does.
So now I've run out of ideas. Hopefully, someone here can help.
To make everything a bit clearer, let me list the app regs involved:
A. Web Api
Was setup via the VS Wizard/dotnet-msidentity tool
has a few App Roles defined
exposes a single API api://<itsownAppId>/access_as_user
B. Interactive Login
manually created
redirect URI for localhost
API Permissions: added WebApi | access_as_user as delegated
C. Non-interactive login/Service Principal
setup manually
is used also for other things by the CI/CD pipeline
has a ClientSecret defined
API Permissions: added WebApi | access_as_user with 2 of the app roles defined for A
has other API Permissions that have nothing to do with this here (for Graph)
granted admin consent for all permissions
The code I use to authenticate is (for the confidential flow):
ConfidentialClientApplicationBuilder.Create(_configuration.ApplicationId)
.WithTenantId(_configuration.Directory)
.WithLogging(Log, LogLevel.Error)
.WithClientSecret(_configuration.ClientSecret)
.Build()
.AcquireTokenForClient(_configuration.Scopes)
.ExecuteAsync();
where the values of _configuration are:
ApplicationId: the appId from app registration C
Directory: the name of my AAD tenant
ClientSecret: the secret from app registration C
Scopes: array of openid, profile and api://<appIdOfWebApiFromC>/access_as_user/.default
So, it turns out that the documentation for appending ./default is not quite clear enough:.You are not meant to append it to the scope, just to the "resource id". And with resource id they mean the "api://" parts without the name of the permission.
So where you normally request api://<webApiAppId>/access_as_user, for the client-credentials flow you have to request api://<webApiAppId>/.default

Token access blocked when posting request from published Azure function

I am struggling to get a token from "https://login.microsoftonline.com/common/oauth2/token" with an Azure function by a post-request. The token will give permissions to access SharePoint though CSOM. Here is my code snippet with the post request:
var clientId = defaultAADAppId;
var body = $"resource={resource}&client_id={clientId}&grant_type=password&username={HttpUtility.UrlEncode(username)}&password={HttpUtility.UrlEncode(password)}";
using (var stringContent = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded"))
{
var result = await httpClient.PostAsync(tokenEndpoint, stringContent);
var tokenResult = JsonSerializer.Deserialize<JsonElement>(result);
var token = tokenResult.GetProperty("access_token").GetString();
}
When testing locally, both when running the function in Visual studio and when I try with Postman, I am able to achieve an access token. However, as soon as I publish the function to my Function app in Azure I receive the following error message:
"AADSTS53003: Access has been blocked by Conditional Access policies. The access policy does not allow token issuance"
I have enabled an app registration in the portal and as mentioned, it all works fine until I publish everything to Azure.
Any ideas on how to solve this?
I got it to work now. First of all I reviewed the CA policies as #CaseyCrookston suggested. What I found out was that our CA policies blocked calls outside the country we operate from. However, the calls from the App registration/Azure function were registered from the Azure data centre location and thus, blocked by our CA policies. When running them locally the calls where registered in my country and therefore no errors were showing while debugging.
My first step was trying to add my Client app to the CA policy, which was not possible. The client/secret authentication that I used based on the suggestions in this CSOM guide by Microsoft prevented the App registration to be whitelisted from the CA policies (Github issue).
Based on this I had to change the authentication to a Certificate-based authentication as suggested here: Access token request with a certificate and here: SO answer. With this I was able to whitelist the App registration in the CA policies and successfully authenticate to the Sharepoint CSOM.
As the error message says, your app is blocked by CA policy. Possible causes can be unknown client app, blocking external IP addresses, etc.
You can perform one of the below workarounds:
Add your Client app to your CA policy.
I wouldn’t recommend this because this affects your security - if you take the risk you could exclude the “Microsoft Azure Management” from your CA policy which blocks unknown clients / requires device state and still protect the sign-in with MFA.
A better approach is to use another OAuth 2.0 and OpenID connect flow like the delegated flow where you sign-in directly within the app, if possible.

New Azure AD application doesn't work until updated through management portal

I have created a new application in Azure AD using the AAD Graph API. (code)
Unfortunately it doesn't let my client access the requested resources until I have been to the application's configuration page in the Azure management portal and made a cosmetic change, and then saved it. After removing the change and saving again, it still works.
The application manifest files before the change + change back steps and after them are completely identical (as in diff.exe says they are the same).
When comparing the JWT tokens returned when the application authenticates, it shows that the post-change access token includes the "roles" section. The entire "roles" section is not present in the access token returned before saving the application in the management portal.
So it seems the Azure management portal does "something" to the application when saving changes. The question is what it is, and can I do the same using the AAD graph API?
There were several issues. Some bugs in the backend on Azure, which have now been fixed, and also some missing calls to the API which I didn't know were necessary.
Thanks to some very helpful people at MS Support, we were able to get it to work.
When creating an application, you need to do the following:
Create an application object.
Setup the RequiredResourceAccess for the application, ie. which permissions the appliation has to Azure Graph API etc. This is what is configured in the portal's "permissions to other applications" settings. You can get the necessary GUIDs by configuring the permissions manually, and then looking in the application's AAD manifest file.
Create a service principal for the application.
Add AppRoleAssignments to the service principal.
The final part is what I was missing before. Even though you have configured RequiredResourceAccess on the application object, the service principal still needs the AppRoleAssignments to actually have permission to access the resources.
When creating the AppRoleAssignments it is a little bit tricky to figure out which PrincipalId to assign, since that is the AAD ObjectId of the service principal for the other resource.
Here is a snippet for adding the AppRoleAssignment to access the Azure AD Graph API. client is an ActiveDirectoryClient instance, and sp is the ServicePrincipal for my application:
// find the azure ad service principal
var aadsp =
client.ServicePrincipals.Where(csp => csp.AppId == "00000002-0000-0000-c000-000000000000")
.ExecuteSingleAsync().Result;
// create the app role assignment
var azureDirectoryReadAssignment = new AppRoleAssignment
{
PrincipalType = "ServicePrincipal",
PrincipalId = Guid.Parse(sp.ObjectId), //
Id = Guid.Parse("5778995a-e1bf-45b8-affa-663a9f3f4d04"), // id for Directory.Read
// azure active directory resource ID
ResourceId = Guid.Parse(aadsp.ObjectId) // azure active directory resource ID
};
// add it to the service principal
sp.AppRoleAssignments.Add(azureDirectoryReadAssignment);
// update the service principal in AAD
await sp.UpdateAsync();
My experience is that you need to wait a short time, maybe 2-3 minutes, before the newly created objects are valid in AAD, and then you can authenticate using the new application.
Apart from RasmusW's answer above, there a few more things that you might have to do depending on what you are trying to achieve.
If you want delegated permissions to work, you also need to add an Oauth2PermissionGrant into Oauth2PermissionGrants collection at the root level. This should have clientId of caller's SPN ObjectId, ResourceId of called SPN's object Id. The Scope value of the Oauth2PermissionGrant is key. It should have space separated values. Each value here comes from the 'Value' property of the Oauth2Permission object on the target SPN.
Additionally you may also need to be in appropriate DirectoryRole e.g. Directory Readers.

Azure AD Graph call for User creation failing with some obscure error

I have been told to raise a question about Azure AD Graph Api here instead of raising it as an issue for the corresponding GitHub sample repository, I hope Azure Graph API team monitors SO and can help us with this github sample issue
Some extra context for the problem:
We are using Microsoft.Azure.ActiveDirectory.GraphClient nuget to create and manage users in our Azure AD test tenant. Following this sample application
we can create and update users in Azure AD via Graph API. That was fine until some moment which happened about 2-3 days ago (not sure about the exact moment, we discovered the problem on Tuesday morning AU time. Last successful run was on Friday last week). It was 100% working code and to confirm that it's not our code - I ran through sample application again - it's broken now too. I've tested it with the latest GraphClient v2.1.0 and original one from sample app which is - v2.0.6
To simplify testing of the problem I've made a LINQ based sample with some secrets redacted ( you need to follow console app sample guide to put in your values if you want to run it, instead of original sample app)
Also here is the Fiddle captures (redacted) of
Authentication request-response (client-request-id: 88b2bbbd-94cd-498d-a147-caad05e16eb7)
User Creation failing Attempt
Few things to note in the fiddler captures - Azure AD Graph API doesn't return refresh token along with access token:
{"expires_in":"3599","token_type":"Bearer","scope":"UserProfile.Read",
"expires_on":"1441183928","not_before":"1441180028","resource":"https://graph.windows.net",
"access_token":"TOKEN WAS HERE"}
I can see the issue with the scope string here, but we are not setting any explict scope in GraphClient when calling for token as per Sample app ( and this code was fine before, as I mentioned early)
User creation response is clear in terms of what happens and why it happens
{"odata.error":
{"code":"Authorization_RequestDenied","message":
{"lang":"en","value":"Insufficient privileges to complete the operation."}
}
}
But it's not clear how to ask for extra permission scopes through any of these AuthenticationContext.AcquireToken Method overloads
Also it's strange that the very same code was fully functional before and is broken now, after some mysterious change?
So I have few questions:
How to add extra scope permissions in GraphClient library to get Graph API token with User Modification enabled. This is probably a band aid fix of the problem.
It looks like Azure AD tries to manage permissions for client apps in the portal. But there is no extra permissions for Native Client type of application. How can I explicitly update app permissions so the existing code can work again. Is it possible at all ?
Can anyone recommend other libraries to interact with GraphAPI, which allow the consumer to explicitly specify scope for the requested token ?
================Update================
Yes, I saw that consent flow documentation. Chasing the issue I've created new Azure AD tenant, added brand new application and added all possible rights, including all available application and delegation ones:
now it looks like this. I also
I can get a token with long list of scopes, e.g.
Directory.AccessAsUser.All
Directory.Read
Directory.Write
User.Read
User.ReadWrite
User.Read.All
User.ReadBasic.All
User.ReadWrite.All
user_impersonation
UserProfile.Read
--cut unrelated scopes--
But it still gives me 403 :
{"odata.error":
{"code":"Authorization_RequestDenied","message":
{"lang":"en","value":"Insufficient privileges to complete the operation."}
}
}
One thing worth to note - in the scope list there is no Directory.ReadWrite.All
But here docs says :
Directory.ReadWrite.All : Read and write directory data
================Update================
PS: Some technical request info for Azure AD engineers:
Authentication request-response has client-request-id: 88b2bbbd-94cd-498d-a147-caad05e16eb7.
Failed Graph call has client-request-id: 882f3918-0ddd-40fe-a558-866997e32b46 in response only
The issue here is that you have not properly configured your application to request the correct permissions to the Graph API.
To do this, you must go into the Azure Management Portal and configure your 'permissions to other applications' to include the scopes your app needs to be able to read/write to the directory.
We have some documentation here which should help you:
https://msdn.microsoft.com/en-us/library/azure/dn132599.aspx (Describes the consent flow)
https://msdn.microsoft.com/Library/Azure/Ad/Graph/api/graph-api-permission-scopes (describes the specific permissions that the graph API exposes that your application will need to request)
I hope this helps!

Categories