7
votes

I am using Azure REST API for fetching Billing Usage and Ratecard details. To acquire Token using AcquireToken() Method, initially I used only Client Id which then asks for User Credentials in login window.

However, I am looking for Non-Interactive Approach, so I used Client Credentials in which I passed Client Id and Client Secret Key.

But it gives "Remote Server returns an error 401 Unauthorized"

When I look into error deeply, I found that it gives error "The access token is from wrong audience or resource"

Please give me any solution using which I can access the API without any user interaction.

Thanks in Advance.

Here is my code:

{
    string token = GetOAuthTokenFromAAD();
    string requestURL = String.Format("{0}/{1}/{2}/{3}",
                   ConfigurationManager.AppSettings["ARMBillingServiceURL"],
                   "subscriptions",
                   ConfigurationManager.AppSettings["SubscriptionID"],
                   "providers/Microsoft.Commerce/RateCard?api-version=2015-06-01-preview&$filter=OfferDurableId eq 'MS-AZR-*****' and Currency eq 'INR' and Locale eq 'en-IN' and RegionInfo eq 'IN'");

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestURL);

    request.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + token);
    request.ContentType = "application/json";
    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
    Console.WriteLine(String.Format("RateCard service response status: {0}", response.StatusDescription));
}

public static string GetOAuthTokenFromAAD()
{
      AuthenticationContext authenticationContext = new AuthenticationContext(string.Format("{0}/{1}",ConfigurationManager.AppSettings["ADALServiceURL"], ConfigurationManager.AppSettings["TenantDomain"]));

      AuthenticationResult result = null;
      ClientCredential uc = new ClientCredential(Client_Id, Secret_Key);
      try
      {
           result = authenticationContext.AcquireToken("https://management.core.windows.net/", uc);
      } 
      return result.AccessToken;
}

//App Config File
<add key="ADALServiceURL" value="https://login.microsoftonline.com" />
<add key="ADALRedirectURL" value="http://*****-authentication.cloudapp.net" />
<add key="ARMBillingServiceURL" value="https://management.core.windows.net" />
<add key="TenantDomain" value="********.onmicrosoft.com" />
<add key="SubscriptionID" value="*******-****-****-****-********" />
<add key="ClientId" value="*******-****-****-****-********" />
1
Please share your code and also the settings for the application you created in Azure AD.Gaurav Mantri
One quick question - Have you added permission to execute Service Management API operations when configuring the application in Azure AD?Gaurav Mantri
Yes. I have provided Permissions to Windows Azure Service Management as Access Azure Service Management (preview) which is the only option available in dropdown under delegated permissions.Tejas
One last question (sorry for asking these questions in piece meal) - When you created the application in Azure AD, you chose Web Application. Correct? Let me try doing the same thing in my Azure AD and see if I can reproduce this behavior.Gaurav Mantri
Please see this link for this: azure.microsoft.com/en-in/documentation/articles/…. You would need to do it at the Subscription level. Also, since you're getting "audience" related error, it wouldn't hurt to see the audience in the token. Do something like this in your code: System.IdentityModel.Tokens.JwtSecurityToken securityToken = new System.IdentityModel.Tokens.JwtSecurityToken(token); and then check the Audiences property in the securityToken. Please see the screenshot: i.stack.imgur.com/j4RWS.pngGaurav Mantri

1 Answers

1
votes

Update: I have also provided these methods as a Reusable Authentication Helper Class Library. You can find the same at this link: Azure Authentication - Authenticating any Azure API Request in your Application

Method 1: To use the password approach non-interactively you need to first follow the below post's section "Authenticate with password - PowerShell": Authenticating a service principal with ARM

Then use the below code snippet to fetch token.

var authenticationContext = new AuthenticationContext(String.Format("{0}/{1}",
                                                                ConfigurationManager.AppSettings["ADALServiceURL"],
                                                                ConfigurationManager.AppSettings["TenantDomain"]));
        var credential = new ClientCredential(clientId: "11a11111-11a1-111a-a111-1afeda2bca1a", clientSecret: "passwordhere");
        var result = authenticationContext.AcquireToken(resource: "https://management.core.windows.net/", clientCredential: credential);

        if (result == null)
        {
            throw new InvalidOperationException("Failed to obtain the JWT token");
        }

        string token = result.AccessToken;

        return token;

Alternatively (Method 2), you can also use the certificate method. In that case use the same link as above but follow section "Authenticate with certificate - PowerShell" from that link. Then use the below code snippet to fetch the token non-interactively:

 var subscriptionId = "1a11aa11-5c9b-4c94-b875-b7b55af5d316";
        string tenant = "1a11111a-5713-4b00-a1c3-88da50be3ace";
        string clientId = "aa11a111-1050-4892-a2d8-4747441be14d";

        var authContext = new AuthenticationContext(string.Format("https://login.windows.net/{0}", tenant));

        X509Certificate2 cert = null;
        X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
        string certName = "MyCert01";

        try
        {
            store.Open(OpenFlags.ReadOnly);
            var certCollection = store.Certificates;
            var certs = certCollection.Find(X509FindType.FindBySubjectName, certName, false);
            //var certs = certCollection.Find(X509FindType.FindBySerialNumber, "E144928868B609D35F72", false);
            if (certs == null || certs.Count <= 0)
            {
                throw new Exception("Certificate " + certName + " not found.");
            }
            cert = certs[0];
        }
        finally
        {
            store.Close();
        }

        var certCred = new ClientAssertionCertificate(clientId, cert);
        var token = authContext.AcquireToken("https://management.core.windows.net/", certCred);
        var creds = new TokenCloudCredentials(subscriptionId, token.AccessToken);
        //var client = new ResourceManagementClient(creds); 
        return token.AccessToken;