This repository has been archived by the owner on Oct 6, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 64
/
device_code_flow.rs
96 lines (82 loc) · 3.42 KB
/
device_code_flow.rs
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
86
87
88
89
90
91
92
93
94
95
96
use azure_sdk_auth_aad::*;
use azure_sdk_storage_blob::prelude::*;
use azure_sdk_storage_core::prelude::*;
use futures::stream::StreamExt;
use oauth2::ClientId;
use std::env;
use std::error::Error;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let client_id =
ClientId::new(env::var("CLIENT_ID").expect("Missing CLIENT_ID environment variable."));
let tenant_id = env::var("TENANT_ID").expect("Missing TENANT_ID environment variable.");
let storage_account_name = std::env::args()
.nth(1)
.expect("please specify the storage account name as first command line parameter");
let client = reqwest::Client::new();
// 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 = begin_authorize_device_code_flow(
&client,
&tenant_id,
&client_id,
&[
&format!(
"https://{}.blob.core.windows.net/user_impersonation",
storage_account_name
),
"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 = Box::pin(device_code_flow.stream());
let mut authorization = None;
while let Some(resp) = stream.next().await {
println!("{:?}", resp);
// if we have the authorization, let's store it for later use.
if let DeviceCodeResponse::AuthorizationSucceded(auth) = resp? {
authorization = Some(auth);
}
}
// remove the option (this is safe since we
// unwrapped the errors before).
let authorization = authorization.unwrap();
println!(
"\nReceived valid bearer token: {}",
&authorization.access_token().secret()
);
if let Some(refresh_token) = authorization.refresh_token().as_ref() {
println!("Received valid refresh token: {}", &refresh_token.secret());
}
// 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_client = client::with_bearer_token(
&storage_account_name,
&authorization.access_token().secret() as &str,
);
// now we enumerate the containers in the
// specified storage account.
let containers = storage_client.list_containers().finalize().await?;
println!("\nList containers completed succesfully: {:?}", containers);
// now let's refresh the token, if available
if let Some(refresh_token) = authorization.refresh_token() {
let refreshed_token =
exchange_refresh_token(&client, &tenant_id, &client_id, None, refresh_token).await?;
println!("refreshed token == {:#?}", refreshed_token);
}
Ok(())
}