I would like to use something along the lines below in rust (see below for working C# code). But I cannot find a way to authenticate in rust because of what the azure_identity
crate in rust implements.
How can I authenticate in rust based on clientID
, tenantID
and clientSecret
as shown here, please?
using System.Threading.Tasks;
using Azure.Core;
using Azure.Identity;
using Azure.Storage.Blobs;
using DotNetEnv;
using Sprache;
using static System.Runtime.InteropServices.JavaScript.JSType;
class Program
{
static async Task Main(string[] args)
{
Env.Load();
//// Retrieve Azure credentials from environment variables
string? clientId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID");
string? tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");
string? clientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET");
string? accountName = Environment.GetEnvironmentVariable("AZURE_ACCOUNT_NAME");
string? containerName = Environment.GetEnvironmentVariable("AZURE_CONTAINER_NAME");
string? prefix = Environment.GetEnvironmentVariable("AZURE_PREFIX");
if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(tenantId) ||
string.IsNullOrEmpty(clientSecret) || string.IsNullOrEmpty(accountName) ||
string.IsNullOrEmpty(containerName))
{
Console.WriteLine("Missing required Azure credentials in .env file.");
return;
}
// Use ClientSecretCredential for authentication
var credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
// Create a BlobServiceClient object
var blobServiceClient = new BlobServiceClient(
new Uri($"https://{accountName}.blob.core.windows.net"),
credential
);
// Get a reference to the container
var containerClient = blobServiceClient.GetBlobContainerClient(containerName);
// List blobs in the specified folder
Console.WriteLine($"Listing blobs in folder {prefix ?? "root"}:");
await foreach (var blobItem in containerClient.GetBlobsAsync(prefix: prefix))
{
// Get just the filename without the path
string fileName = Path.GetFileName(blobItem.Name);
Console.WriteLine($"File name: {fileName}");
}
}
}
EDIT As suggested by the comment I tried to make it work with version 0.21. Here is how far I got:
use azure_identity::ClientSecretCredential;
use azure_storage_blobs::prelude::*;
use std::env;
use dotenv::dotenv;
use anyhow::{Result, anyhow};
use futures::stream::StreamExt; // For stream processing
#[tokio::main]
async fn main() -> Result<()> {
dotenv().ok();
let client_id = env::var("AZURE_CLIENT_ID").map_err(|_| anyhow!("AZURE_CLIENT_ID not set"))?;
let client_secret = env::var("AZURE_CLIENT_SECRET").map_err(|_| anyhow!("AZURE_CLIENT_SECRET not set"))?;
let tenant_id = env::var("AZURE_TENANT_ID").map_err(|_| anyhow!("AZURE_TENANT_ID not set"))?;
let account_name = env::var("AZURE_ACCOUNT_NAME").map_err(|_| anyhow!("AZURE_ACCOUNT_NAME not set"))?;
let container_name = env::var("AZURE_CONTAINER_NAME").map_err(|_| anyhow!("AZURE_CONTAINER_NAME not set"))?;
let azure_prefix = env::var("AZURE_PREFIX").map_err(|_| anyhow!("AZURE_PREFIX not set"))?;
let credentials = std::sync::Arc::new(ClientSecretCredential::new(tenant_id, client_id, client_secret));
let container_client = ClientBuilder::new(account_name, credentials)
.container_client(container_name);
// List blobs in the container
let mut stream = container_client.list_blobs().into_stream();
println!("Blobs in container '{}':", container_name);
while let Some(result) = stream.next().await {
match result {
Ok(response) => {
for blob in response.blobs.blobs() {
println!(" - {}", blob.name);
}
}
Err(error) => {
eprintln!("Error listing blobs: {}", error);
return Err(error.into()); // Convert Azure Storage Error to Anyhow Error
}
}
}
Ok(())
}
with this cargo.toml:
[package]
name = "azure_test"
version = "0.1.0"
edition = "2024"
[dependencies]
azure_storage_blobs = "0.14"
azure_identity = "0.21"
azure_core = "0.21"
tokio = { version = "1", features = ["full"] }
dotenv = "0.15"
anyhow = "1.0"
futures = "0.3"
reqwest = { version = "0.11", features = ["json", "stream"] }
But it seems these credentials aren't compatible with the storage blob
trait `From<Arc<ClientSecretCredential>>` is not implemented for `azure_storage::authorization::StorageCredentials`
how can I make this work pls?
Azure_identity in rust doesn't implement ClientSecretCredential class.
According to this Document,
The azure_identity
package in Rust does not implement ClientSecretCredential
,However, you can obtain an OAuth2 token manually using an HTTP request and use it for authentication with Azure Storage.
Code:
use azure_storage::StorageCredentials;
use azure_storage_blobs::prelude::*;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use tokio;
use futures::stream::StreamExt;
#[derive(Serialize)]
struct TokenRequest<'a> {
grant_type: &'a str,
client_id: &'a str,
client_secret: &'a str,
scope: &'a str,
}
#[derive(Deserialize)]
struct TokenResponse {
access_token: String,
}
async fn get_oauth_token(tenant_id: &str, client_id: &str, client_secret: &str) -> Result<String, Box<dyn std::error::Error>> {
let token_url = format!("https://login.microsoftonline.com/{}/oauth2/v2.0/token", tenant_id);
let params = TokenRequest {
grant_type: "client_credentials",
client_id,
client_secret,
scope: "https://storage.azure.com/.default",
};
let client = Client::new();
let res = client.post(&token_url)
.form(¶ms)
.send()
.await?
.json::<TokenResponse>()
.await?;
Ok(res.access_token)
}
#[tokio::main]
async fn main() -> azure_core::Result<()> {
let tenant_id = "xxxx";
let client_id = "xxxx";
let client_secret = "xxx";
let storage_account = "xxx";
let container_name = "xxt";
// Get OAuth2 Token
let token = get_oauth_token(tenant_id, client_id, client_secret).await.unwrap();
let credentials = StorageCredentials::bearer_token(token);
let blob_service = BlobServiceClient::new(storage_account, credentials);
let container_client = blob_service.container_client(container_name);
let mut stream = container_client.list_blobs().into_stream();
while let Some(response) = stream.next().await {
match response {
Ok(response) => {
for blob in response.blobs.blobs() {
println!("Blob name: {}", blob.name);
}
}
Err(err) => {
eprintln!("Error listing blobs: {:?}", err);
}
}
}
Ok(())
}
The above rust code fetches an OAuth2 token using the client credentials flow, then uses it to authenticate with Azure Blob Storage and list all blobs in a specified container.
Note: Make sure the Service principal has Storage Blob Data Contributor to access the storage account.
Output:
Blob name: data.gz
Blob name: demo.pdf
Blob name: foo/bar/industry.csv
Blob name: foo/bar/sample5000.xlsx
Blob name: foo/demo/cert_1.key
Blob name: foo/demo/cert_2.key
Blob name: foo/test/Octkb 24.md
Blob name: foo/test/Octkb2 24.md
Blob name: large-file.csv
Blob name: sample.mp4
Blob name: sample.txt
Blob name: sample5000.xlsx
Blob name: samplehub1/venkatesan/0xxx4.avro
Blob name: samplehub1/venkatesan/xxxx.avro
Blob name: scenery.jpg
Blob name: test.xlsx
Blob name: test/demo/cert_0.cer
Blob name: test/demo/cert_1.cer
Blob name: testblob.txt
Update:
cargo.toml
:
[dependencies]
tokio = { version = "1", features = ["full"] }
azure_core = "0.20"
azure_identity = "0.20"
azure_storage = "0.20"
azure_storage_blobs = "0.20"
reqwest = { version = "0.11", features = ["json"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
futures = "0.3"