7

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="*******-****-****-****-********" />
Tejas
  • 83
  • 11
  • Please share your code and also the settings for the application you created in Azure AD. – Gaurav Mantri Jan 01 '16 at 10:20
  • One quick question - Have you added permission to execute Service Management API operations when configuring the application in Azure AD? – Gaurav Mantri Jan 01 '16 at 11:18
  • 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 Jan 01 '16 at 11:27
  • 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 Jan 01 '16 at 11:43
  • Yes.I chose Add an application my organization is developing and in the Add Application Wizard, I chose Web application and/or Web API type. – Tejas Jan 01 '16 at 11:47
  • Hmmm....I tried the same with an application in my Azure AD and it worked just fine. I can fetch the rate card (part of billing API) perfectly fine. Can you share the rest of the code? May be something's missing there. – Gaurav Mantri Jan 01 '16 at 12:26
  • Could you tell me what all steps you followed while creating and configuring Web App in AD. I think something is missing there only or you have used different code for authentication. – Tejas Jan 01 '16 at 12:56
  • I didn't do anything special in configuring the App in AD. It was standard Web App. Here's my code: ` var authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/tenant.onmicrosoft.com"); AuthenticationResult result = null; ClientCredential uc = new ClientCredential("b6dac948-...-ef9c10cbb7ff", "SaZbc...ieO2w="); result = authenticationContext.AcquireToken("https://management.core.windows.net/", uc); var token = result.AccessToken;` – Gaurav Mantri Jan 01 '16 at 13:13
  • I then used the token to access the API in Google Postman and was able to see the data. Initially I got 403 error because I didn't grant any permission to the user on my Azure Subscription but once I granted the application `Reader` permissions on the Subscription (using new portal), all was well. – Gaurav Mantri Jan 01 '16 at 13:15
  • Thanks. Could you please provide the steps how you granted the access to this Web App as I am not able to find the Web App in the Azure New Portal. – Tejas Jan 01 '16 at 13:40
  • 1
    Please see this link for this: https://azure.microsoft.com/en-in/documentation/articles/resource-group-create-service-principal-portal/. 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: http://i.stack.imgur.com/j4RWS.png – Gaurav Mantri Jan 01 '16 at 13:48
  • Thanks a lot for helping. Adding Application to the user in the new portal worked fine... – Tejas Jan 04 '16 at 05:32

1 Answers1

1

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;
Aman Sharma
  • 1,930
  • 1
  • 17
  • 31