OAUTH2 In Microsoft Fabric

In my previous article, I discussed how to leverage the newly introduced service principal support in Azure. In that article I used Azure SDK to create tokens to authenticate through service principal. This made me think if its possible to use custom OAUTH2 tokens in Microsoft Fabric instead of Azure SDK generated tokens through service principal.

So lets find out.

The Setup

First, ensure that the Service Principal setting is enabled on your Fabric tenant.

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

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

and register a new application

I have registered the application with name Fabric OAUTH2. 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.

The Code

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";

Method to create a OAUTH2 token. The uri used for token generation is https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token

   private static async Task<string> GetOAuthToken(string tenantId, string clientId, string clientSecret)
   {
       var Endpoint = $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token";
       var client = new HttpClient();

       var request = new HttpRequestMessage(HttpMethod.Get, Endpoint)
       {
           Content = new FormUrlEncodedContent(new[]
           {
           new KeyValuePair<string, string>("client_id", clientId),
           new KeyValuePair<string, string>("scope", "https://api.fabric.microsoft.com/.default"),
           new KeyValuePair<string, string>("client_secret", clientSecret),
           new KeyValuePair<string, string>("grant_type", "client_credentials")
       })
       };

       HttpResponseMessage response = await client.SendAsync(request);
       response.EnsureSuccessStatusCode();
       var content = await response.Content.ReadAsStringAsync();           
       var tokenResponse = System.Text.Json.JsonDocument.Parse(content);
       string accessToken = tokenResponse.RootElement.GetProperty("access_token").GetString();
       return accessToken;
   }

Call to the above method

  string accesstoken = await GetOAuthToken(tenantId, clientId, clientSecret);

Create a method to fetch details of all workspaces on the tenant

  private static async Task<string> ListAllWorkSpaces(string accessToken, string oneLakeContainerUrl)
  {
      var httptclient = new HttpClient();
      httptclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
      HttpResponseMessage response = await httptclient.GetAsync(oneLakeContainerUrl);
      response.EnsureSuccessStatusCode();
      return await response.Content.ReadAsStringAsync();
  }

Call to the above method and output the details of the workspaces.

 response = await ListAllWorkSpaces(accesstoken, endpoint);
 string prettyJson = JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(response), 
      new JsonSerializerOptions { WriteIndented = true }
  );

There is only one workspace on the tenant

If we want to fetch the details of all the lakehouses under the workspace, we set the value of the endpoint uri to the following

  private static string endpoint = $"https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/lakehouses";

The complete code

using Azure.Identity;
using Azure.Storage.Files.DataLake;
using System.Net.Http.Headers;
using System.Text.Json;

namespace Fabric_OAUTH2
{
    internal class Program
    {
        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";

        static async Task Main(string[] args)
        {
            string accesstoken = await GetOAuthToken(tenantId, clientId, clientSecret);
            response = await ListAllWorkSpaces(accesstoken, endpoint);
            string prettyJson = JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(response),
            new JsonSerializerOptions { WriteIndented = true });
            Console.WriteLine(prettyJson);
        }

        private static async Task<string> GetOAuthToken(string tenantId, string clientId, string clientSecret)
        {
            var Endpoint = $"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token";
            var client = new HttpClient();

            var request = new HttpRequestMessage(HttpMethod.Get, Endpoint)
            {
                Content = new FormUrlEncodedContent(new[]
                {
         new KeyValuePair<string, string>("client_id", clientId),
         new KeyValuePair<string, string>("scope", "https://api.fabric.microsoft.com/.default"),
         new KeyValuePair<string, string>("client_secret", clientSecret),
         new KeyValuePair<string, string>("grant_type", "client_credentials")
     })
            };

            HttpResponseMessage response = await client.SendAsync(request);
            response.EnsureSuccessStatusCode();
            var content = await response.Content.ReadAsStringAsync();
            var tokenResponse = System.Text.Json.JsonDocument.Parse(content);
            string accessToken = tokenResponse.RootElement.GetProperty("access_token").GetString();
            return accessToken;
        }

        private static async Task<string> ListAllWorkSpaces(string accessToken, string oneLakeContainerUrl)
        {
            var httptclient = new HttpClient();
            httptclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
            HttpResponseMessage response = await httptclient.GetAsync(oneLakeContainerUrl);
            response.EnsureSuccessStatusCode();
            return await response.Content.ReadAsStringAsync();
        }
    }
}

Conclusion

Though I personally would prefer using Azure.SDK for token generation for service principal in Fabric, using OAuth2 as an industry-standard protocol for authorization would ensure that applications can interact securely with Fabric services.

If an web or 3rd party application needs to interact with Fabric services I would prefer using OAUTH2 and if Azure services need to interact with Fabric I would preferAzure.SDK. As service principal is still a fairly new feature introduced in Fabric, I think it would premature to comment on the pro and cons of the two approaches but again that’s just my personal opinion.

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