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 !!!
Subscribe to my newsletter
Read articles from Sachin Nandanwar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by