locked
Web API authentication RRS feed

  • Question

  • I recently made a MVC5 app that called a "Azure Scheduler" via REST.  I registered the scheduler as an app in active directory.  Then in my client i created a static http client that requested a bearer token then update the client header,  from this i used it to contact the web api methods successfully.

    Now i uplifted some code to a new client.  I also published a templated azure webapi, then registered that in active directory and set its authorization to use it.
    Now when i use my http client it seems to access the token successfully, but when making http calls i get a 401 unauthorized every time.

    the code:

    public static class SchedulerHttpClient
        {
            const string SPNPayload = "resource={0}&client_id={1}&grant_type=client_credentials&client_secret={2}";
    
            private static Lazy<Task<HttpClient>> _Client = new Lazy<Task<HttpClient>>(async () =>
            {
                string baseAddress = ConfigurationManager.AppSettings["BaseAddress"];
                var client = new HttpClient();
                client.BaseAddress = new Uri(baseAddress);
                await MainAsync(client).ConfigureAwait(false);
                return client;
            });
    
            public static Task<HttpClient> ClientTask => _Client.Value;
    
            private static async Task MainAsync(HttpClient client)
            {
                string tenantId = ConfigurationManager.AppSettings["AzureTenantId"];
                string clientId = ConfigurationManager.AppSettings["AzureClientId"];
                string clientSecret = ConfigurationManager.AppSettings["AzureClientSecret"];
    
                string token = await AcquireTokenBySPN(client, tenantId, clientId, clientSecret).ConfigureAwait(false);
    
                client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token); //TODO ssmith: const or localization
            }
    
            private static async Task<string> AcquireTokenBySPN(HttpClient client, string tenantId, string clientId, string clientSecret)
            {
                var payload = String.Format(SPNPayload,
                                            WebUtility.UrlEncode(ConfigurationManager.AppSettings["ARMResource"]),
                                            WebUtility.UrlEncode(clientId),
                                            WebUtility.UrlEncode(clientSecret));
    
                var body = await HttpPost(client, tenantId, payload).ConfigureAwait(false);
                return body.access_token;
            }
    
            private static async Task<dynamic> HttpPost(HttpClient client, string tenantId, string payload)
            {
                var address = String.Format(ConfigurationManager.AppSettings["TokenEndpoint"], tenantId);
                var content = new StringContent(payload, Encoding.UTF8, "application/x-www-form-urlencoded");
                using (var response = await client.PostAsync(address, content).ConfigureAwait(false))
                {
                    if (!response.IsSuccessStatusCode)
                    {
                        Console.WriteLine("Status:  {0}", response.StatusCode);
                        Console.WriteLine("Content: {0}", await response.Content.ReadAsStringAsync().ConfigureAwait(false));
                    }//TODO: start removing tests
    
                    response.EnsureSuccessStatusCode();
    
                    return await response.Content.ReadAsAsync<dynamic>().ConfigureAwait(false);
                }
            }
        }
    And the controller that calls it,

     public async Task<ActionResult> About()
            {
                _client = await SchedulerHttpClient.ClientTask;
                var response = await _client.GetAsync($"/api/Jobs");
                response.EnsureSuccessStatusCode();
                //ar responseContent = await response.Content.ReadAsAsync<T>().ConfigureAwait(false);
    
                ViewBag.Message = "Your application description page.";
    
                return View();
            }

    And finally some of the settings (obviously cant give away some details only urls.

    <add key="AzureSubscription" value="sub" />
    <add key="AzureTenantId" value="tentantid" />
    <add key="AzureClientId" value="client id" />
    <add key="AzureClientSecret" value="secret" />
    
    <add key="ARMResource" value="https://management.core.windows.net/" />
        <add key="TokenEndpoint" value="https://login.windows.net/{0}/oauth2/token" />
        <add key="BaseAddress" value="https://rgwebapi.azurewebsites.net" />
    

    Now my question is, is my method incorrect to access my web api (even though it works for azure scheduler web api.
    Or is it a case of settings may be incorrect such as the ARM resource (do i even need it now?).

    Many thanks


    Thursday, March 30, 2017 9:18 AM