Error when using AD to work with Azure Management API - c#

I'm using Microsoft.WindowsAzure.Management and Microsoft.IdentityModel.Clients.ActiveDirectory packages trying to work with Azure Management API from C# code, but when I try to retrieve some data from it, I'm always getting the error:
ForbiddenError: The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription.
There's the code sample I'm using:
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.WindowsAzure.Management;
var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("https://login.windows.net/mytenant");
var cc = new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential("azure-app-id", "azure-app-secret");
var token = await authContext.AcquireTokenAsync("https://management.core.windows.net/", cc);
var tokenCred = new Microsoft.Azure.TokenCloudCredentials(token.AccessToken);
var client = new ManagementClient(tokenCred);
// this is where I get the error:
var subscriptions = await client.Subscriptions.GetAsync(CancellationToken.None);

I believe you're getting this error is because the Service Principal (or in other words the Azure AD application) does not have permission on your Azure Subscription. You would need to assign a role to this Service Principal.
Please see this link regarding how you can assign a role in an Azure Subscription to a Service Principal: https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal#assign-application-to-role.
Once you do that, the error should go away.

I can reproduce this issue too. And to list the subscription, we need to use the SubscriptionClient instead of ManagementClient. Here is the code which works well for me:
var token = "";
var tokenCred = new Microsoft.Azure.TokenCloudCredentials(token);
var subscriptionClient = new SubscriptionClient(tokenCred);
foreach (var subscription in subscriptionClient.Subscriptions.List())
{
Console.WriteLine(subscription.SubscriptionName);
}
Note:To make the code work, we need to acquire token using the owner of the subscription instead of the certificate.

Related

From localhost C# Web API - Accessing secret from Azure KeyVault throws error Invalid Issuer

I am trying from my local web api, to retrieve secret from KeyVault using Azure.Identity lib.
but it throws Invalid Issuer. Giving below the code I am using
My current code
var client = new SecretClient(new Uri("key-vault-url"), new DefaultAzureCredential()); ==> line #1
var secret = client.GetSecret("DicomSecret").Value; ==> line #2
As soon as it parses line#2 it throws the below error.
What I have tried
I have added my Azure credential in the KeyVault thru' Add Access Policy
Tried using ManagedIdentityCredential instead of DefaultAzureCredential in line#1
Also tried using VisualStudioCredential instead of DefaultAzureCredential in line#1
I also read that I can be using EnvironmentCredential for which I need to provide AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET but I am not exactly sure how to and what to include for this - I do not have access to AAD.
Please let me know how to resolve this issue.
Since I was trying to connect to Azure from my local development environment (VS 2019) it was expecting additional credentials.
So from my dev environment (localhost) I had to use
DefaultAzureCredentialOptions VisualStudioTenantId along with SecretClient.
var tenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
DefaultAzureCredentialOptions options = new DefaultAzureCredentialOptions()
{
VisualStudioTenantId = tenantId,
SharedTokenCacheTenantId = tenantId
};
var client = new SecretClient(
new Uri(key-vault-url),
new DefaultAzureCredential(options)
);
The above helped me to execute from my local but after deploying it to Azure Ap Service the below line of code was sufficient. So I used the above code only for my local testing.
var client = new SecretClient(new Uri("key-vault-url"), new DefaultAzureCredential());
This is my code and it seems that there's no difference with yours.
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace test0430callingapi.Controllers
{
public class HelloController : Controller
{
public async Task<string> IndexAsync()
{
const string secretName = "clientsecret";
var kvUri = "https://keyvaultname.vault.azure.net/";
var a = new DefaultAzureCredential();
var client = new SecretClient(new Uri(kvUri), a);
var secret = await client.GetSecretAsync(secretName);
string secretVaule = secret.Value.Value;
return secretVaule ;
}
}
}
Then I think you may try to check the DefaultAzureCredential. When running the code in visual studio, we need to make sure that you've signed in with the user which has access permission to azure key vault by Add Access Policy in portal. Or maybe you've added the user, then you could check if has added enough permission for the user.
And if it also failed, you may try another way to access key vault by api. More details you can refer to this answer.

Access token not containing SCP (roles) claims via Microsoft Graph

I'm using the Microsoft Graph SDK to get an access token for my application (not a user) in order to read from sharepoint. I've been following this document, as well as posted this SO question. The code in the linked SO is the same. I was able to add application permissions as well as grant them (by pressing the button) in azure portal. The problem is, the token that comes back to be used does not contain any roles / scp claims in it. Therefore when using the token, I get the "Either scp or roles claim need to be present in the token" message.
Just to be certain, the only value for my scope that I pass when getting the access token is: https://graph.microsoft.com/.default. I don't pass anything else like Sites.ReadWrite.All (I get an exception if I add that scope anyway). I'm not sure how to continue troubleshooting and any help would be appreciated.
Edit: added code using the graph SDK shown below:
var client = new ConfidentialClientApplication(id, uri, cred, null, new SessionTokenCache());
var authResult = await client.AcquireTokenForClientAsync(new[] {"https://graph.microsoft.com/.default"});
var token = authResult.AccessToken;
var graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider(async request => {request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token)}));
var drives = await graphServiceClient.Sites[<sharepoint_host>].SiteWithPath(<known_path>).Drives.Request().GetAsync();
Seems like doing the app initialization in a different way is the solution. Instead of this:
var client = new ConfidentialClientApplication(id, uri, cred, null, new SessionTokenCache());
do this:
var app = new ConfidentialClientApplication(ClientId, Authority, RedirectUri, credentials, null, new TokenCache());
The problem is, the token that comes back to be used does not contain
any roles / scp claims in it.
If you can not find any roles/scp claims in the decoded access token. You need to check the permission in Azure portal again.
The decoded access token should contain the roles you granted.
Login Azure portal->click Azure Active Directory->click App registrations(preview)->find your application.
Click your application->API permissions->check if you have grant admin consent for your application. If not, click 'Grant admin consent'.
The code for getting access token. You can find more details here.
//authority=https://login.microsoftonline.com/{tenant}/
ClientCredential clientCredentials;
clientCredentials = new ClientCredential("{clientSecret}");
var app = new ConfidentialClientApplication("{clientId}", "{authority}", "{redirecturl}",
clientCredentials, null, new TokenCache());
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
AuthenticationResult result = null;
result = app.AcquireTokenForClientAsync(scopes).Result;
Console.WriteLine(result.AccessToken);

Permissions required to create subscriptions on a mailbox folder while accessing GraphAPI without user

I created an app with the Application Permissions MailboxSettings.ReadWrite.
I then built a Web API to create subscriptions to mailbox folders.
I am successfully able to get the Auth Token and listen to the test notification from the app. But when I try to create a subscription I get the following error:
{
Code: ExtensionError
Message: Operation: Create; Exception: [Status Code: Unauthorized; Reason: Unauthorized]
}
This is how I am creating a subscription:
var subscription = new Subscription
{
Resource = $"users/{CurrentUserId}/mailFolders('Inbox')/messages",
ChangeType = "created,updated",
NotificationUrl = ConfigurationManager.AppSettings["ida:NotificationUrl"],
ClientState = Guid.NewGuid().ToString(),
ExpirationDateTime = DateTime.UtcNow + new TimeSpan(0, 0, 15, 0)
};
var newSubscription = await graphClient.Subscriptions.Request().AddAsync(subscription);
My question is, which permissions does my App need to have create a subscription?
I am using Microsoft Graph access without a user.
https://learn.microsoft.com/en-us/graph/auth-v2-service. I want to run my API unattended.
According to the doc, the Mail.Read permission will be enough. You need admin consent the permission for the AD App, see this link.

Azure invalid AccessToken

i am trying to use Microsoft.Azure.Management.Resources library to manage some Azure resources. I have registered app in Azure AD and i gave it all permissons. I took its ApplicationId and Secret + TennantId and SubscriptionId and tried to obtaion AccessToken like this:
var clientCredential = new ClientCredential(_model.DeploymentDetails.CliendId, _model.DeploymentDetails.ClientSecret);
var context = new AuthenticationContext("https://login.windows.net/"+model.DeploymentDetails.TennantId);
_accessToken = context.AcquireTokenAsync("https://management.azure.com/", clientCredential).Result.AccessToken;
_resourceManagementClient = new ResourceManagementClient(new TokenCloudCredentials(_model.DeploymentDetails.SubscriptionId,_accessToken));
I get some AccessToken. BUT when i try to use it like this:
var x = _resourceManagementClient.ResourceGroups.List(...);
I get this error:
Additional information: InvalidAuthenticationToken: The received access token is not valid: at least one of the claims 'puid' or 'altsecid' or 'oid' should be present. If you are accessing as application please make sure service principal is properly created in the tenant.
Any ideas?
Thank you very much.
As far as I know, Microsoft.Azure.Management.Resources.dll that implements the ARM API. We need to assign application to role, after that then we can use token in common. More information about how to assign application to role please refer to the article .This blog also has more detail steps to get AceessToken.

Email Audit API - 403 Forbidden

I am trying to download a user's mailbox using the Email Audit API. I am getting a 403 Forbidden response to this code (the error occurs on the last line, the call to the UploadPublicKey method):
var certificate = new X509Certificate2(System.Web.HttpRuntime.AppDomainAppPath + "key.p12", "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { "https://apps-apis.google.com/a/feeds/compliance/audit/" }
}.FromCertificate(certificate));
credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Wait();
DebugLabel.Text = credential.Token.AccessToken;
var requestFactory = new GDataRequestFactory("My App User Agent");
requestFactory.CustomHeaders.Add(string.Format("Authorization: Bearer {0}", credential.Token.AccessToken));
AuditService aserv = new AuditService(strOurDomain, "GoogleMailAudit");
aserv.RequestFactory = requestFactory;
aserv.UploadPublicKey(strPublicKey);
I have created the service account in the Developers Console and granted the Client ID access to https://apps-apis.google.com/a/feeds/compliance/audit/ in the Admin console.
Seems to me like the account should have all the permissions it needs, yet it doesn't. Any idea what I am missing?
OK, so I gave up on trying to make it work with a service account even though that is what Google's documentation would lead you to believe is the correct way to do it. After emailing Google support, I learned I could just use OAuth2 for the super user account that created the application on the developer's console.
So then I worked on getting an access token for offline access (a refresh token) by following the process outlined here:
Youtube API single-user scenario with OAuth (uploading videos)
and then taking that refresh token and using it with this code:
public static GOAuth2RequestFactory RefreshAuthenticate(){
OAuth2Parameters parameters = new OAuth2Parameters(){
RefreshToken = "<YourRefreshToken>",
AccessToken = "<AnyOfYourPreviousAccessTokens>",
ClientId = "<YourClientID>",
ClientSecret = "<YourClientSecret>",
Scope = "https://apps-apis.google.com/a/feeds/compliance/audit/",
AccessType = "offline",
TokenType = "refresh"
};
OAuthUtil.RefreshAccessToken(parameters);
return new GOAuth2RequestFactory(null, "<YourApplicationName>", parameters);
}
which is code from here https://stackoverflow.com/a/23528629/5215904 (Except I changed the second to last line... for whatever reason the code shared did not work until I made that change).
So there I was finally able to get myself an access token that would allow me access to the Email Audit API. From there everything was a breeze once I stopped trying to mess around with a service account.

Categories

Resources