Search code examples
asp.net-web-apioauth-2.0asp.net-coreadalazure-active-directory

Aspnet core web api protected with Azure


I have a web api in my organization built with aspnet core. We want to publish that api to be consumed by an android app, a mvc5 app and an aspnet core mvc6 app. How can I configure the web api in azure so that the apps that consume it don't ask to login. The web apps, are already protected with azure, but when I protect the web api with azure I get a 401 when I make a request to it. I don't know how to configure the app in azure or the code I must configure in the api. I've read a lot but I don't find a way to acomplish this. All I want is to login in my web app, and the web app starts to ask data to the web api through ajax. I should send in the ajax request some sort of bareer token, but i don`t know what config i must do in azure and in the apps. I hope you can help me.


Solution

  • After you protected the web API with Azure AD, we need to send to access token with request for the web API for authorization. And we can get the access token when the users call the web API from web app. Here is the code to acquire the token in the web app for your reference:

    public async Task<IActionResult> Index()
    {
            AuthenticationResult result = null;
            List<TodoItem> itemList = new List<TodoItem>();
    
            try
            {
                string userObjectID = (User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier"))?.Value;
                AuthenticationContext authContext = new AuthenticationContext(Startup.Authority, new NaiveSessionCache(userObjectID, HttpContext.Session));
                ClientCredential credential = new ClientCredential(Startup.ClientId, Startup.ClientSecret);
                result = await authContext.AcquireTokenSilentAsync(Startup.TodoListResourceId, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
    
                //
                // Retrieve the user's To Do List.
                //
                HttpClient client = new HttpClient();
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, TodoListBaseAddress + "/api/todolist");
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
                HttpResponseMessage response = await client.SendAsync(request);
    
                //
                // Return the To Do List in the view.
                //
                if (response.IsSuccessStatusCode)
                {
                    List<Dictionary<String, String>> responseElements = new List<Dictionary<String, String>>();
                    JsonSerializerSettings settings = new JsonSerializerSettings();
                    String responseString = await response.Content.ReadAsStringAsync();
                    responseElements = JsonConvert.DeserializeObject<List<Dictionary<String, String>>>(responseString, settings);
                    foreach (Dictionary<String, String> responseElement in responseElements)
                    {
                        TodoItem newItem = new TodoItem();
                        newItem.Title = responseElement["title"];
                        newItem.Owner = responseElement["owner"];
                        itemList.Add(newItem);
                    }
    
                    return View(itemList);
                }
                else
                {
                    //
                    // If the call failed with access denied, then drop the current access token from the cache, 
                    //     and show the user an error indicating they might need to sign-in again.
                    //
                    if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                    {
                        var todoTokens = authContext.TokenCache.ReadItems().Where(a => a.Resource == Startup.TodoListResourceId);
                        foreach (TokenCacheItem tci in todoTokens)
                            authContext.TokenCache.DeleteItem(tci);
    
                        ViewBag.ErrorMessage = "UnexpectedError";
                        TodoItem newItem = new TodoItem();
                        newItem.Title = "(No items in list)";
                        itemList.Add(newItem);
                        return View(itemList);
                    }
                }
            }
            catch (Exception ee)
            {
                if (HttpContext.Request.Query["reauth"] == "True")
                {
                    //
                    // Send an OpenID Connect sign-in request to get a new set of tokens.
                    // If the user still has a valid session with Azure AD, they will not be prompted for their credentials.
                    // The OpenID Connect middleware will return to this controller after the sign-in response has been handled.
                    //
                    return new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme);
                }
    
                //
                // The user needs to re-authorize.  Show them a message to that effect.
                //
                TodoItem newItem = new TodoItem();
                newItem.Title = "(Sign-in required to view to do list.)";
                itemList.Add(newItem);
                ViewBag.ErrorMessage = "AuthorizationRequired";
                return View(itemList);
            }
    
    
            //
            // If the call failed for any other reason, show the user an error.
            //
            return View("Error");
    }
    

    And below is the code sample which use JwtBearerAppBuilderExtensions to add OpenIdConnect Bearer authentication capabilities to an HTTP application pipeline for the web API to verify the token:

    public class Startup
    {
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
            {
                // Add the console logger.
                loggerFactory.AddConsole(LogLevel.Debug);
    
                // Configure the app to use Jwt Bearer Authentication
                app.UseJwtBearerAuthentication(new JwtBearerOptions
                {
                    AutomaticAuthenticate = true,
                    AutomaticChallenge = true,
                    Authority = String.Format(Configuration["AzureAd:AadInstance"], Configuration["AzureAD:Tenant"]),
                    Audience = Configuration["AzureAd:Audience"],
                });
            }
    }
    

    The full code sample you can refer here.

    Note: to run this sample successfully, we need to modify the Title and Owner to lowercase title, owner in the ToDoController of web app:

     foreach (Dictionary<String, String> responseElement in responseElements)
     {
         TodoItem newItem = new TodoItem();
         newItem.Title = responseElement["title"];
         newItem.Owner = responseElement["owner"];
         itemList.Add(newItem);
     }