I am using the following code to try to programmatically register an application in Azure Active Directory:
var application = azure.ActiveDirectoryApplications.Define(applicationName)
.WithSignOnUrl(url)
.WithIdentifierUrl(url)
.WithAvailableToOtherTenants(false)
.DefinePasswordCredential(id)
.WithPasswordValue(secret)
.Attach()
.Create();
Where azure is an instance of Microsoft.Azure.Management.Fluent.Azure.
When I run the above to create an Azure Active Directory Application, an Microsoft.Azure.Management.Fluent.Azure exception is thrown with the message Operation returned an invalid status code 'Forbidden'. Creation of other Azure resources (like resource groups and app services) work just fine.
Looking at the exception details, I can see that a request is made to the following endpoint:
https://graph.windows.net/{myTenantId}/applications?api-version=1.6
The following is in the response body:
{"odata.error":{"code":"Authorization_RequestDenied","message":{"lang":"en","value":"Insufficient privileges to complete the operation."}}}
Since the bodies says "Insufficient privileges to complete the operation", it appears to be a simple permission issue, but I have granted the following permissions (while signed in as a global administrator) for the Microsoft.Azure.ActiveDirectory API for the application that's running the code:
Access the directory as the signed-in user
Read and write directory data
Are these privileges not enough? What am I missing? As I said, creation of other resources using the fluent API works just fine.
The scope Directory.AccessAsUser.All and Directory.ReadWrite.All User.Read is sufficient permission to create applications in the Azure Active Directory. Since you doesn't provide how you construct the azure instance, I would provide a working code sample:
static void Main(string[] args)
{
var url = "http://adfei.onmicrosoft.com/appFluent";
var id = "abc";
var secret = "secret";
var applicationName = "appFluent";
var credFile = new AzureCredentials(new UserLoginInformation
{
ClientId = "{appId of native application}",
UserName = "{userName}",
Password = "{password}"
},
"adfei.onmicrosoft.com", AzureEnvironment.AzureGlobalCloud);
IAzure azure = Azure.Authenticate(credFile).WithDefaultSubscription();
var application = azure.ActiveDirectoryApplications.Define(applicationName)
.WithSignOnUrl(url)
.WithIdentifierUrl(url)
.WithAvailableToOtherTenants(false)
.DefinePasswordCredential(id)
.WithPasswordValue(secret)
.Attach()
.Create();
Console.Read();
}
And please ensure the scope is include in the access token to ensure that you have the permission for this operation. You can capture the request via Fiddler to check the token and decode the token from this site to check scp claims in the access token.
Related
I am getting the following error when trying to access the directory api by getting a list of users
Google.Apis.Auth.OAuth2.Responses.TokenResponseException: Error:"unauthorized_client", Description:"Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.", Uri:""
Based on my previous googling efforts and reading stack overflow I am not sure what is causing this. As far as I am aware I have everything set up correctly. I have it setup to use the drive api in a very similar fashion and that works perfectly.
My service account does have domain wide delegation which is why I think it may have something to do with the second part of the error. Any ideas what could be causing this?
protected async virtual Task<DirectoryService?> GetDirectoryService()
{
if (currentDirectory == null)
{
string[] scopes = new string[] { DirectoryService.Scope.AdminDirectoryUser };
var initializer = new ServiceAccountCredential.Initializer(configuration["GoogleServiceAccount:AccountEmail"]){Scopes = scopes, User = configuration["GoogleServiceAccount:UserEmail"] };
var cred = new ServiceAccountCredential(initializer.FromPrivateKey(configuration["GoogleServiceAccount:SecretKey"]));
currentDirectory = new DirectoryService(new BaseClientService.Initializer { HttpClientInitializer = cred, ApplicationName = "DriveAPI" });
}
return currentDirectory;
User = configuration["GoogleServiceAccount:UserEmail"]
User is the user on your domain that you want to delegate as not the service accounts email address.
update
Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested
This error message in my exprence normally means that you are using code that does not match the type of credetinals you are using.
There are serval types of authorization, service account, Oauth installed and oauth web (lets ignore mobile for now). The code used for these credentials is different.
So if you use a service account key file with code designed for an Ouath2 installed app. You will normally get "Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested"
The code I normally use for delegation with a service account is as follows. it looks similar to yours so i'm inclined to agree that you are using service account code. Which means to me you are probably using the wrong key file. I would double check. Open it it should say "type": "service_account".
// Load the Service account credentials and define the scope of its access.
var credential = GoogleCredential.FromFile(PathToServiceAccountKeyFile)
.CreateWithUser("user#mydomain.com")
.CreateScoped(new[] {DriveService.ScopeConstants.Drive});
My suggestion is to now double check and ensure that you are using the service account key file from google cloud console that was created by a user on your domain, and that you configured domain wide deligation for and have added the admin sdk scopes for remember the OAuth Scopes for admin have to be set, as well configuring an authorized user.
what I want to do: use the Azure REST API from a custom tool.
What I did:
I created an app registration named CDTester and a secret for it in my default directory. Then I went to the subscription and added the role Contributor to CTTester.
I got a config object cfg loaded from user-secrets with the application id and secret of CDTester and with the tenant id of my default directory.
Now I try to authenticate like this:
var app = ConfidentialClientApplicationBuilder
.Create(cfg.ApplicationId)
.WithTenantId(cfg.LoginTenantId)
.WithClientSecret(cfg.ApplicationSecret)
.Build();
var authResult = await
app.AcquireTokenForClient(new[] {"https://management.azure.net/.default"})
.ExecuteAsync();
The .ExecuteAsynccall fails with
"'
AADSTS500011: The resource principal named https://management.azure.net was not found in the tenant named . This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant."
As a (weak) alternative, I added the Azure Service Management delegated permission to CDTester, gave Admin Consent, switched the code to:
var app = PublicClientApplicationBuilder.Create(cfg.ApplicationId)
.WithTenantId(cfg.LoginTenantId)
.WithDefaultRedirectUri()
.Build();
var authResult = await app
.AcquireTokenInteractive(new[] { "https://management.azure.net/user_impersonation" })
.ExecuteAsync();
ran it, logged in with my own user (who's of course global administrator) and got the same message, this time in the browser right after login.
Changing the scope to "https://management.azure.net/.default" in this flow doesn't change anything either.
So, obviously somehow I need to "install the resource principal https://management.azure.net" into my tenant - but for the life of me, I cannot find how.
As mentioned in the comment, please change https://management.azure.net to https://management.azure.com/.
Essentially there is no such thing as https://management.azure.net. To get an access token so that you can execute Azure Resource Manager API, you will need to acquire a token for https://management.azure.com/ resource.
I'm having issues using bloblserviceClient with azure Ad
I'm logged into account "X" in visual studio and also tools => optios => azure service authentication = account "X"
This is the following code
var uri = new Uri("https://testblob112.blob.core.windows.net/order");
var cred = new DefaultAzureCredential();
var client = new BlobClient(uri, cred);
await client.UploadAsync("test.txt");
User X also has Storage contributor access to the storage account order.
I'm getting the following error
ure.Identity.AuthenticationFailedException: SharedTokenCacheCredential authentication failed: AADSTS9002332: Application 'e406a681-f3d4-42a8-90b6-c2b029497af1'(Azure Storage) is configured for use by Azure Active Directory users only. Please do not use the /consumers endpoint to serve this request.
Am I missing anything?
Key access may be disabled.
Can you check these two options under Configuration in your Storage Account?
What I want to do is have the user log in to the AAD prompt on their Windows Desktop machines, so I get a Bearer token that will work with my Azure Function.
I've followed the tutorial from this article on adatum but it only covers the application permission (not delegated permissions)
I already have an Azure Function that is set up for Azure AD
authentication.
I already have a client app that I registered (under
App Registrations).
I've configured it to use delegated permissions
for the Azure Function.
Here's my client code:
var clientId = "client id for my console app";//console app
var clientUrl = new Uri("https://login.microsoftonline.com/common/oauth2/nativeclient");
var tenant = "tenantid here";
string authority = "https://login.windows.net/" + tenant;
string resource = "https://myaadProtectedFunc.azurewebsites.net";
AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);
var pp = new PlatformParameters(PromptBehavior.Auto);
var token = authenticationContext.AcquireTokenAsync(resource, clientId, clientUrl,
pp, UserIdentifier.AnyUser).Result;
Console.WriteLine("Got the token: {0}", token.AccessToken);
I get an error saying "[my client app] needs permission to access resources in your organization that only an admin can grant. Please ask an admin to grant permission to this app before you can use it."
Is there some other way to get a Bearer token that will work with my Azure Function?
I did a test and your code does work if you set up the azure side correctly. most likely in the azure function you do not have it set up correctly.
in the azure function did you set up the service principal?
eg. function app -> platform features -> authentication / authorization -> App Service Authentication to ON -> select azure active directory -> express -> create. -> press ok. -> save.
then in your app registration, you will now see 2. the app reg for your client, and app reg for your function app. in the app reg for your client go to api permissions and add the app registration for your function app with the user impersonation selected.
finally make sure your enterprise application has the user/groups you want to be allowed to access for each of the enterprise apps. (client and function app registration)
Hope that helps.
Ok it turns out that I don't need to make a separate client application at all.
I can just use the Client ID of the Azure Function.
The thing is that you will have to go to 'Advance' instead of 'Express' because the library Microsoft.Identity.Client uses v2.0 tokens.
This is to configure your Azure Function - keep all fields the same, but add a /v2.0 to the Issuer URL:
This is the code to get the delegated bearer token for the Azure Function, which uses the v2.0 token - I don't know how to change it to use the v1 token:
string[] scopes = new string[] { "profile", "email", "openid" };
string ClientId = [clientId of Azure Function];
string Tenant = [tenantId];
string Instance = "https://login.microsoftonline.com/";
var _clientApp = PublicClientApplicationBuilder.Create(ClientId)
.WithAuthority($"{Instance}{Tenant}")
.WithDefaultRedirectUri()
.Build();
var accounts = _clientApp.GetAccountsAsync().Result;
var authResult = _clientApp.AcquireTokenInteractive(scopes)
.WithAccount(accounts.FirstOrDefault())
.WithPrompt(Prompt.SelectAccount)
.ExecuteAsync().Result;
var bearerTokenForAzureFunction = authResult.IdToken;
Now I don't understand the need to register a client application at all if I can get the delegated bearer token this way...
I am trying to create a simple example of a console Application that requires a valid user. The Application is a simple Console Application:
static void Main(string[] args)
{
string authority = "https://login.windows.net/----------------.onmicrosoft.com";
AuthenticationContext context = new AuthenticationContext(authority);
Console.WriteLine("Context created");
string resource = "ConsoleApp1";
string clientId = "------------------------------------";
string redirectUri = "http://localhost";
var parameters = new PlatformParameters(PromptBehavior.RefreshSession);
context.TokenCache.Clear();
// Throws AggregateException--> AADSTS50105: The signed in user is not assigned to a role for the application
var token = Task.Run(() => context.AcquireTokenAsync(resource, clientId, new Uri(redirectUri), parameters)).Result;
Console.WriteLine("Hello, authorized user");
}
The Application starts, presents the sign-in window, and then throws the exception
AADSTS50105: The signed in user is not assigned to a role for the application
Over in the Azure Portal, I've registered ConsoleApp1 as a native Application.
Under Required Permissions, I've checkmarked:
Sign in and read user profile
Access the Directory as the signed-in user
Other than that, I'm stumped, the error Message tells me that I need to assign some kind of role to the user, but I cannot find this anywhere in the Application's settings. Can someone help shed some light on this please?
You were specify the incorrect resource(ConsoleApp1). The resource should should represents the resource you have granted to this application and which you acquire the access token for.
It seems that you grant Azure AD Graph's permission to this application, did you want to acquire the access token to use the Azure Graph REST?
If I understood correctly, you can replace the resource using https://graph.windows.net.
More detail about integrating with Azure AD, you can refer here.