1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
use azure_core::{error::ErrorKind, Error};
use azure_identity::{device_code_flow, refresh_token};
use azure_storage::prelude::*;
use azure_storage_blobs::prelude::*;
use futures::stream::StreamExt;
use std::env::{args, var};
#[tokio::main]
async fn main() -> azure_core::Result<()> {
let client_id = var("CLIENT_ID").expect("Missing CLIENT_ID environment variable.");
let tenant_id = var("TENANT_ID").expect("Missing TENANT_ID environment variable.");
let storage_account_name = args()
.nth(1)
.expect("please specify the storage account name as first command line parameter");
let http_client = azure_core::new_http_client();
// the process requires two steps. The first is to ask for
// the code to show to the user. This is done with the following
// function. Notice you can pass as many scopes as you want.
// Since we are asking for the "offline_access" scope we will
// receive the refresh token as well.
// We are requesting access to the storage account passed as parameter.
let device_code_flow = device_code_flow::start(
http_client.clone(),
&tenant_id,
&client_id,
&[
&format!("https://{storage_account_name}.blob.core.windows.net/user_impersonation"),
"offline_access",
],
)
.await?;
// now we must show the user the authentication message. It
// will point the user to the login page and show the code
// they have to specify.
println!("{}", device_code_flow.message());
// now we poll the auth endpoint until the user
// completes the authentication. The following stream can
// return, besides errors, a success meaning either
// Success or Pending. The loop will continue until we
// get either a Success or an error.
let mut stream = device_code_flow.stream();
let authorization = loop {
match stream.next().await {
Some(Ok(authorization)) => break authorization,
Some(Err(_)) => continue,
None => {
return Err(Error::with_message(ErrorKind::Credential, || {
"device flow stream ended unexpectedly"
}))
}
}
};
// we can now spend the access token in other crates. In this example we are
// creating an Azure Storage client using the access token.
let storage_credentials =
StorageCredentials::bearer_token(authorization.access_token().clone());
let blob_service_client = BlobServiceClient::new(storage_account_name, storage_credentials);
// now we enumerate the containers in the specified storage account.
let containers = blob_service_client
.list_containers()
.into_stream()
.next()
.await
.expect("stream failed")?;
println!("\nList containers completed succesfully: {containers:?}");
// If we want to use the refresh token to get a new access token (such as if
// we wanted to bump the expiry window on the token), we can do the
// following
if let Some(refresh_token) = authorization.refresh_token() {
let refreshed_token =
refresh_token::exchange(http_client, &tenant_id, &client_id, None, refresh_token)
.await?;
println!("refreshed token == {refreshed_token:#?}");
}
Ok(())
}