MSAL For Microsoft Fabric

A few days ago, I published an article on OAuth2 authorization in Microsoft Fabric, which you can find here.

One of the key pieces of feedback I received from Mathias Thierbach (Linkedin) was that instead of creating my own OAuth2 implementation, I should have used the MSAL library. Coincidentally, I had already done that when I published an article on Fabric APIs here.

So this article serves as a more refined version of my previous one focusing on implementing OAuth2 authentication through the MSAL library.

Need for MSAL

MSAL provides a unified API for acquiring access tokens and handling authentication which reduces complexity and under the hood it implements OAUTH2 authentication without the need for implementation of your own version of OAUTH2.

The SetUp

The set up is similar to what was done in this article ,but I would still repeat the steps here.

Ensure that the Service Principal setting is enabled on your Fabric tenant.

To get started we need to register the application at entra.microsoft.com

Once logged in, navigate to Applications >>App registrations >> New registration

I registered the application with name Fabric MSAL. We would required Client ID and Tenant ID values to reference in the code from the registered app.

also Client Secrets will be required.

Now that the feature is enabled in the next step we would grant access to the registered app to connect to the workspaces. In this case we would assign contributor access for the workspace.

In your Fabric tenant, under the workspace, select Manage access and assign Contributor access to the application principal that was created in the earlier step.

After adding the service principal you should be able to see the added user in the list of users of a workspace.

The Code

As usual, create a new Console application and declare a bunch of variables

private static string response = "";
 private static string clientId = "Client Id of the Registered App";
 private static string tenantId = "Tenant Id of the Registered App";
 private static string clientSecret = "Client Secret of the Registered App";
 private static string endpoint = $"https://api.fabric.microsoft.com/v1/workspaces";
 private static string[] scopes = new string[] { "https://api.fabric.microsoft.com/.default" };

We then create a separate class called Service that handles the HttpRequests

 public class Service
 {
     private static readonly HttpClient client = new HttpClient();
     public static HttpClient Client => client;
     public static async Task<string> GetAsync(string url, string Token)
     {
         Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token);
         HttpResponseMessage ht = await client.GetAsync(url);
         ht.EnsureSuccessStatusCode();
         string content = await ht.Content.ReadAsStringAsync();
         return content;
     }
 }

Create a method to handle the MSAL token generation

private static async Task<string> MSALToken(string tenantId, string clientId, string clientSecret)
{
    ConfidentialClientApplicationBuilder ConfidentialClientAppBuilder =
           ConfidentialClientApplicationBuilder.Create(clientId)
           .WithTenantId(tenantId)
           .WithClientSecret(clientSecret);

    IConfidentialClientApplication ConfidentialClientApplication = ConfidentialClientAppBuilder.Build();
    AuthenticationResult result = await ConfidentialClientApplication.AcquireTokenForClient(scopes)
                                                                     .ExecuteAsync()
                                                                     .ConfigureAwait(false);
    return result.AccessToken;
}

Create a method to return a list of workspaces on the fabric tenant authenticated through the token generated.

  private static async Task ListAllWorkSpaces(string accessToken, string oneLakeContainerUrl)
  {
      responsename = await Service.GetAsync(oneLakeContainerUrl, accessToken);
      string prettyJson = JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(responsename),
      new JsonSerializerOptions { WriteIndented = true });
      Console.WriteLine(prettyJson);
  }

Call both the method’s through the Main method of the application

static async Task Main(string[] args)
{
    string accesstoken = await MSALToken(tenantId, clientId, clientSecret);
    ListAllWorkSpaces(accesstoken, endpoint).GetAwaiter().GetResult();
}

As there is only one workspace on the tenant it displays the details of that workspace in the output.

Complete Code

using Microsoft.Identity.Client;
using System.Net.Http.Headers;
using System.Text.Json;
namespace MSAL
{
    internal class Program
    {
        private static string responsename = "";
        private static string clientId = "Client Id of the Registered App";
        private static string tenantId = "Tenant Id of the Registered App";
        private static string clientSecret = "Client Secret of the Registered App";
        private static string endpoint = $"https://api.fabric.microsoft.com/v1/workspaces";
        private static string[] scopes = new string[] { "https://api.fabric.microsoft.com/.default" };

        static async Task Main(string[] args)
        {
            string accesstoken = await MSALToken(tenantId, clientId, clientSecret);
            ListAllWorkSpaces(accesstoken, endpoint).GetAwaiter().GetResult();

        }

        private static async Task ListAllWorkSpaces(string accessToken, string oneLakeContainerUrl)
        {
            responsename = await Service.GetAsync(oneLakeContainerUrl, accessToken);
            string prettyJson = JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(responsename),
            new JsonSerializerOptions { WriteIndented = true });
            Console.WriteLine(prettyJson);
        }

        private static async Task<string> MSALToken(string tenantId, string clientId, string clientSecret)
        {
            ConfidentialClientApplicationBuilder ConfidentialClientAppBuilder =
                   ConfidentialClientApplicationBuilder.Create(clientId)
                   .WithTenantId(tenantId)
                   .WithClientSecret(clientSecret);

            IConfidentialClientApplication ConfidentialClientApplication = ConfidentialClientAppBuilder.Build();
            AuthenticationResult result = await ConfidentialClientApplication.AcquireTokenForClient(scopes)
                                                                             .ExecuteAsync()
                                                                             .ConfigureAwait(false);
            return result.AccessToken;
        }

    }

    public class Service
    {
        private static readonly HttpClient client = new HttpClient();
        public static HttpClient Client => client;
        public static async Task<string> GetAsync(string url, string Token)
        {
            Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token);
            HttpResponseMessage ht = await client.GetAsync(url);
            ht.EnsureSuccessStatusCode();
            string str = await ht.Content.ReadAsStringAsync();
            return str;
        }
    }
}

Conclusion

Implementing MSAL for apps security needs can benefit from a more robust, secure, and scalable approach to authentication, eliminating the need to reinvent the wheel while adhering to best practices recommended by Microsoft. This articles aim was to guide through the improved process for integrating OAuth2 authorization in your Fabric tenant.

Thanks for reading !!!

0
Subscribe to my newsletter

Read articles from Sachin Nandanwar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sachin Nandanwar
Sachin Nandanwar