Azure-Samples / active-directory-dotnet-webapp-webapi-openidconnect-aspnetcore

An ASP.NET Core web application that authenticates Azure AD users and calls a web API using OAuth 2.0 access tokens.
119 stars 97 forks source link

Abstract API Token request #34

Closed eisenreich closed 6 years ago

eisenreich commented 6 years ago

Hey! Do you have any suggestions how to abstract the API token request in the controller to avoid the boilerplate each time I do a request to the API?

kalyankrishna1 commented 6 years ago

@eisenreich Please elaborate a bit more on what boilerplate you are referring to?

eisenreich commented 6 years ago

Sorry for this inaccuracy.

I'm talking about this part:

  // GET: /<controller>/
  public async Task<IActionResult> Index()
  {
      AuthenticationResult result = null;
      List<TodoItem> itemList = new List<TodoItem>();

      try
      {
          // Because we signed-in already in the WebApp, the userObjectId is know
          string userObjectID = (User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier"))?.Value;

          // Using ADAL.Net, get a bearer token to access the TodoListService
          AuthenticationContext authContext = new AuthenticationContext(AzureAdOptions.Settings.Authority, new NaiveSessionCache(userObjectID, HttpContext.Session));
          ClientCredential credential = new ClientCredential(AzureAdOptions.Settings.ClientId, AzureAdOptions.Settings.ClientSecret);
          result = await authContext.AcquireTokenSilentAsync(AzureAdOptions.Settings.TodoListResourceId, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));

          // Retrieve the user's To Do List.
          HttpClient client = new HttpClient();
          HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, AzureAdOptions.Settings.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);
          }

          //
          // 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)
          {
              return ProcessUnauthorized(itemList, authContext);
          }
      }
      catch (Exception)
      {
          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");
  }

When requesting some data form the API to just every time this code is very redundant.

But I found a good solution from a different sample: Check out this. This doesn't comply to 100 percent, but the way how he handles the cache and request the access token works with charm.

jmprieur commented 6 years ago

@eisenreich : I'll provide something better in the coming weeks

eisenreich commented 6 years ago

Thank you alot!