Enable Authentication

At a minimum, any web applications that you want to integrate with BarTender Cloud must provide support for users to sign in and sign out. The following procedures demonstrate how to make the necessary changes to the sample LibrarianWebApp application to support sign-in and sign-out operations. These changes enable the web application to support authentication and the identity provider that is responsible for creating your access token.

ClosedAbout the LibrarianWebApp Application

The LibrarianWebApp application is a generic C# web application that you will configure so that it can interact with the BarTender Cloud REST API. This model-view-controller (MVC) sample application was built by using Microsoft Visual Studio 2022 and .NET Core 6.0.

When you decompress this file, you will find a standard MVC web application. After you open the enclosed solution file in Visual Studio, you can compile and run it to confirm that everything is working. When you run the application, it should resemble the following image.

ClosedStep 1: Update the appsettings.json File

The LibrarianWebApp project’s appsettings.json file must be updated so that it can provide the information that is needed when the web application and its users are authenticated.

To do this, copy the following lines of code, and then update the ClientID and ClientSecret values with the values that you received when you registered the application with BarTender Cloud.

Copy
  "BarTenderCloudCluster": "https://am1.bartendercloud.com",
  "ClientId": "<your client id goes here>",
  "ClientSecret": "<your client secret goes here>",
  "Audience": "https://BarTenderCloudServiceApi",

Note:  "BarTenderCloudCluster" refers to the specific regional BarTender Cloud data center that the web service will interact with. Possible values include but are not limited to the following:

  • https://am1.bartendercloud. com/.well-known/openid-configuration

  • https://eu1.bartendercloud. com/.well-known/openid-configuration

  • https://ap1.bartendercloud. com/.well-known/openid-configuration

Paste the updated lines of code into the appsettings.json file as shown.

The ClientID and ClientSecret values are read by the startup.cs file when it configures your service. (For more information about these values, refer to the "Important Terms and Definitions" section of the C# Web-Based Application topic.)

ClosedStep 2: Update the startup.cs File

The project's update startup.cs file must be updated so that the web application middleware can determine which Auth0 identity provider is used to issue access tokens. However, because the authorization system is based on OpenID Connect, you must first include that NuGet dependency. Then, you can update the startup.cs file.

Install the NuGet dependency

  1. In Visual Studio, in the Solution Explorer pane, right-click the Dependencies node in the project, and then select Manage Nuget Packages. The NuGet Package Manager opens.

  2. In the Search field, enter Microsoft.AspNetCore.Authentication.OpenIdConnect, and then select Microsoft.AspNetCore.Authentication.OpenIdConnect in the package list.

  3. In the pane to the right of the list, select 6.0.15 in the Version list, and then click Install.

    Caution: Microsoft.AspNetCore.Authentication.OpenIdConnect v. 6.0.15 is required, because the project is using Microsoft .NET Core 6.0.

  4. When the Preview Changes dialog appears, asking you to confirm your changes, click OK.

  5. When the License Acceptance dialog appears, click I Accept.

After the dependency is installed, you can update the startup.cs file.

Update the startup.cs file

  1. Open the startup.cs file.

  2. At the top of the file, paste the following lines so that the OpenID Connect models and methods can be made available.

    Copy
    using Microsoft.AspNetCore.Authentication.OpenIdConnect;
    using Microsoft.IdentityModel.Protocols.OpenIdConnect;
    using Newtonsoft.Json;
  3. Under "public IConfiguration Configuration { get; }", paste the following line to track the authentication provider.

    Copy

    public static string ClaimsIssuer { get; set; } = string.Empty;
  4. Add the following new method to the Startup class. This method retrieves the current authentication provider configuration, which simplifies initialization.

    Copy
    void RetrieveAuthenticationConfiguration(string barTenderCloudCluster)
    {
      HttpClient httpClient = new HttpClient();
      string url = barTenderCloudCluster + (!barTenderCloudCluster.EndsWith('/') ? "/" : "") + ".well-known/openid-configuration";
      HttpResponseMessage msg = httpClient.GetAsync(url).Result;
      if (!msg.IsSuccessStatusCode)
          throw new Exception("Failure to retrieve OIDC discovery document");

      string result = msg.Content.ReadAsStringAsync().Result;
      IDictionary<string, object> dictionary = JsonConvert.DeserializeObject<IDictionary<string, object>>(result)!;
      var i = (string)dictionary["issuer"];
      ClaimsIssuer = i.EndsWith('/') ? i.TrimEnd(new char[] { '/' }) : i;
    }
  5. At the top of the ConfigureServices(IServiceCollection services) method, paste the following block of code.

    Copy

    string barTenderCloudCluster = Configuration["BarTenderCloudCluster"];

    RetrieveAuthenticationConfiguration(barTenderCloudCluster);

    // Add authentication services.
    services.AddAuthentication(options =>
    {
         options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
         options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
         options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddOpenIdConnect("BarTenderCloud", options =>
    {
         // Set the authority to your identity provider domain.
         options.Authority = ClaimsIssuer;

         // Configure the identity provider client ID and client secret.
         options.ClientId = Configuration["ClientId"];
         options.ClientSecret = Configuration["ClientSecret"];

         // Set the response type to "code."
         options.ResponseType = OpenIdConnectResponseType.Code;

         // Configure the scope.
         options.Scope.Clear();
         options.Scope.Add("openid");
         options.Scope.Add("user:query");

         // Set the callback path, so that the identity provider will call back to http://localhost:44300/callback.
         options.CallbackPath = new PathString("/callback");

         // Configure the claims issuer to be the identity provider.
         options.ClaimsIssuer = ClaimsIssuer;

         // Save tokens to AuthenticationProperties (required so that access_token is a JWT).
         options.SaveTokens = true;

         options.Events = new OpenIdConnectEvents
         {
            // Handle the logout redirection. 
            OnRedirectToIdentityProviderForSignOut = (context) =>
            {
                var logoutUri =
                   $"{ClaimsIssuer}/v2/logout?client_id={Configuration["ClientId"]}";

                var postLogoutUri = context.Properties.RedirectUri;
                if (!string.IsNullOrEmpty(postLogoutUri))
                {
                   if (postLogoutUri.StartsWith("/"))
                   {
                       // Transform to absolute.
                       var request = context.Request;
                       postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase +
                                      postLogoutUri;
                    }

                    logoutUri += $"&returnTo={Uri.EscapeDataString(postLogoutUri)}";
                 }

                 context.Response.Redirect(logoutUri);
                 context.HandleResponse();

                 return Task.CompletedTask;
             },

             // (Required so that access_token is a JWT.)
             OnRedirectToIdentityProvider = context =>
             {
                 // The context's ProtocolMessage can be used to pass along additional query parameters
                 // to the identity provider's /authorize endpoint.
                 // 
                 // Set the audience query parameter to the API identifier to ensure that the returned access tokens can be used
                 // to call protected endpoints on the corresponding API.
                 context.ProtocolMessage.SetParameter("audience", Configuration["Audience"]);

                 return Task.FromResult(0);
             }
         };
    });
  6. In the Configure() method at the bottom of the file, paste the following two lines as shown.

    Copy
             app.UseAuthentication();
             app.UseAuthorization();