0
votes

Instead of connection strings, SAS token, and other approaches, how can we achieve the functionality using the "Service Principal (OAuth)" authentication.

Service Principal generally provides "Client ID", "Client Secret" and "Tenant ID" values from Azure.

1

1 Answers

2
votes

Steps to resolve this problem is as follows:

  • Generate OAuth token
  • Create token credentials object.
  • Get the Azure Cloud Container Reference.
  • Get blob reference, read to stream and convert to base 64 bytes
  • Display the bytes array image in Angular 6+ page

Namespaces:

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Blob;

Step 1: Generate OAuth token - Using client ID and client secret, generate the access token. We will use this later for authentication.

public string GetUserOAuthToken()
{
    const string ResourceId = "https://storage.azure.com/";
    const string AuthInstance = "https://login.microsoftonline.com/{0}/";
    string authority = string.Format(CultureInfo.InvariantCulture, AuthInstance, ConfigHelper.AzStorAccTenantId);
    AuthenticationContext authContext = new AuthenticationContext(authority);
    var clientCred = new ClientCredential(ConfigHelper.AzStorAccClientId, ConfigHelper.AzStorAccClientSecret);
    AuthenticationResult result = authContext.AcquireTokenAsync(ResourceId, clientCred).Result;
    return result.AccessToken;
}

Step 2: Create token credentials object

public bool GenerateTokenCredentials()
{
    if (string.IsNullOrEmpty(authenticationError))
    {
        string authenticationResult = GetUserOAuthToken();
        if (string.IsNullOrEmpty(authenticationResult))
        {
            return false;
        }
        else
        {
            _tokenCredentials = new TokenCredential(authenticationResult);
            //_tokenCredentials is a global object
            return true;
        }
    }
    else
    {
        return false;
    }
}

Step 3: Get Azure Cloud Container Reference

public CloudBlobContainer CreateCloudBlobContainer()
{
    try
    {
        GenerateTokenCredentials();
        StorageCredentials storageCredentials = new StorageCredentials(_tokenCredentials);
        CloudStorageAccount cloudStorageAccount = new CloudStorageAccount(storageCredentials, ConfigHelper.AzStorAccName, ConfigHelper.AzStorageEndPoint, useHttps: true);
        CloudBlobClient blobClient = cloudStorageAccount.CreateCloudBlobClient();
        return blobClient.GetContainerReference(ConfigHelper.AzStorAccBlobContName);
    }
    catch (Exception ex)
    {

    }
    return null;
}

Step 4: Get blob reference, read to stream, and convert to base 64 bytes

public async Task<string> DownloadFile(string blobFileName)
{
    try
    {
        CloudBlobContainer blobContainer = CreateCloudBlobContainer();
        var blob = blobContainer.GetBlobReference(blobFileName);
        blob.FetchAttributes();
        var stream = await blob.OpenReadAsync();
        var byteData = ReadFully(stream);
        string byteImageUrl = string.Format("data:image/{0};base64," + Convert.ToBase64String(byteData), GetBlobImageExtension(blobFileName));
        return byteImageUrl;
    }
    catch (Exception ex)
    {

    }
    return string.Empty;
}

Helper Private Methods

private byte[] ReadFully(Stream input)
{
    byte[] buffer = new byte[16 * 1024];
    using (MemoryStream ms = new MemoryStream())
    {
        int read;
        while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
        {
            ms.Write(buffer, 0, read);
        }
        return ms.ToArray();
    }
}

private string GetBlobImageExtension(string blobFileName)
{
    string extension = Path.GetExtension(blobFileName).Trim().ToLower().Replace(".", string.Empty);
    string formattedExtensionForByteArray = !string.IsNullOrEmpty(extension) ? extension.Equals("svg") ? extension + "+xml" : extension : string.Empty;
    return formattedExtensionForByteArray;
}

Step 5: Display the bytes array image in Angular 6+

<img [src]="LogoFileName" class="logo" />
  • Typescript
LogoFileName: SafeResourceUrl;
this.LogoFileName = this.sanitizer.bypassSecurityTrustResourceUrl(responseLogoFileName);

This is a complete working example for using the Azure Blobs container images and displaying them without downloading them to the drive.