Skip to content
This repository was archived by the owner on Oct 6, 2020. It is now read-only.

Commit 1995aac

Browse files
author
Francesco Cogno
authored
Merge pull request #303 from MindFlavor/issue/290/aad_refresh_token
Azure AAD: Exchange refresh token support
2 parents a08a649 + 7b73034 commit 1995aac

File tree

9 files changed

+114
-17
lines changed

9 files changed

+114
-17
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
[![Build Status](https://travis-ci.org/MindFlavor/AzureSDKForRust.svg?branch=master)](https://travis-ci.org/MindFlavor/AzureSDKForRust) [![Coverage Status](https://coveralls.io/repos/MindFlavor/AzureSDKForRust/badge.svg?branch=master&service=github)](https://coveralls.io/github/MindFlavor/AzureSDKForRust?branch=master) ![stability-unstable](https://img.shields.io/badge/stability-unstable-yellow.svg)
77

8-
[![tag](https://img.shields.io/github/tag/mindflavor/AzureSDKForRust.svg)](https://github.com/MindFlavor/AzureSDKForRust/tree/aad_0.45.1) [![release](https://img.shields.io/github/release/mindflavor/AzureSDKForRust.svg)](https://github.com/MindFlavor/AzureSDKForRust/releases/tag/aad_0.45.1) [![commitssince](https://img.shields.io/github/commits-since/mindflavor/AzureSDKForRust/aad_0.45.1)](https://github.com/MindFlavor/AzureSDKForRust/commits/master)
8+
[![tag](https://img.shields.io/github/tag/mindflavor/AzureSDKForRust.svg)](https://github.com/MindFlavor/AzureSDKForRust/tree/aad_0.46.0) [![release](https://img.shields.io/github/release/mindflavor/AzureSDKForRust.svg)](https://github.com/MindFlavor/AzureSDKForRust/releases/tag/aad_0.46.0) [![commitssince](https://img.shields.io/github/commits-since/mindflavor/AzureSDKForRust/aad_0.46.0)](https://github.com/MindFlavor/AzureSDKForRust/commits/master)
99

1010
[![GitHub contributors](https://img.shields.io/github/contributors/MindFlavor/AzureSDKForRust.svg)](https://github.com/MindFlavor/AzureSDKForRust/graphs/contributors)
1111

azure_sdk_auth_aad/examples/device_code_flow.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use futures::stream::StreamExt;
55
use oauth2::ClientId;
66
use std::env;
77
use std::error::Error;
8-
use std::sync::Arc;
98

109
#[tokio::main]
1110
async fn main() -> Result<(), Box<dyn Error>> {
@@ -31,7 +30,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
3130
&client_id,
3231
&[
3332
&format!(
34-
"https://{}.blob.core.windows.net/.default",
33+
"https://{}.blob.core.windows.net/user_impersonation",
3534
storage_account_name
3635
),
3736
"offline_access",
@@ -49,7 +48,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
4948
// return, besides errors, a success meaning either
5049
// Success or Pending. The loop will continue until we
5150
// get either a Success or an error.
52-
let mut stream = Box::pin(device_code_flow.stream(&client));
51+
let mut stream = Box::pin(device_code_flow.stream());
5352
let mut authorization = None;
5453
while let Some(resp) = stream.next().await {
5554
println!("{:?}", resp);

azure_sdk_auth_aad/src/device_code_flow.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use azure_sdk_core::errors::AzureError;
44
use futures::stream::unfold;
55
use log::debug;
66
pub use oauth2::{ClientId, ClientSecret};
7+
use std::borrow::Cow;
78
use std::convert::TryInto;
8-
use std::sync::Arc;
99
use std::time::Duration;
1010
use url::form_urlencoded;
1111

@@ -21,7 +21,9 @@ pub struct DeviceCodePhaseOneResponse<'a> {
2121
// from the Azure answer. They will be added
2222
// manually after deserialization
2323
#[serde(skip)]
24-
tenant_id: &'a str,
24+
client: Option<&'a reqwest::Client>,
25+
#[serde(skip)]
26+
tenant_id: Cow<'a, str>,
2527
// we store the ClientId as string instead of
2628
// the original type because it does not
2729
// implement Default and it's in another
@@ -30,17 +32,22 @@ pub struct DeviceCodePhaseOneResponse<'a> {
3032
client_id: String,
3133
}
3234

33-
pub async fn begin_authorize_device_code_flow<'a, 'b>(
35+
pub async fn begin_authorize_device_code_flow<'a, 'b, T>(
3436
client: &'a reqwest::Client,
35-
tenant_id: &'a str,
37+
tenant_id: T,
3638
client_id: &'a ClientId,
3739
scopes: &'b [&'b str],
38-
) -> Result<DeviceCodePhaseOneResponse<'a>, AzureError> {
40+
) -> Result<DeviceCodePhaseOneResponse<'a>, AzureError>
41+
where
42+
T: Into<Cow<'a, str>>,
43+
{
3944
let mut encoded = form_urlencoded::Serializer::new(String::new());
4045
let encoded = encoded.append_pair("client_id", client_id.as_str());
4146
let encoded = encoded.append_pair("scope", &scopes.join(" "));
4247
let encoded = encoded.finish();
4348

49+
let tenant_id = tenant_id.into();
50+
4451
debug!("encoded ==> {}", encoded);
4552

4653
let url = url::Url::parse(&format!(
@@ -69,6 +76,7 @@ pub async fn begin_authorize_device_code_flow<'a, 'b>(
6976
expires_in: device_code_reponse.expires_in,
7077
interval: device_code_reponse.interval,
7178
message: device_code_reponse.message,
79+
client: Some(client),
7280
tenant_id,
7381
client_id: client_id.as_str().to_string(),
7482
})
@@ -92,7 +100,6 @@ impl<'a> DeviceCodePhaseOneResponse<'a> {
92100

93101
pub fn stream<'b>(
94102
&'b self,
95-
client: &'b reqwest::Client,
96103
) -> impl futures::Stream<Item = Result<DeviceCodeResponse, DeviceCodeError>> + 'b + '_ {
97104
#[derive(Debug, Clone, PartialEq)]
98105
enum NextState {
@@ -123,7 +130,9 @@ impl<'a> DeviceCodePhaseOneResponse<'a> {
123130
let encoded = encoded.append_pair("device_code", &self.device_code);
124131
let encoded = encoded.finish();
125132

126-
let result = match client
133+
let result = match self
134+
.client
135+
.unwrap()
127136
.post(&uri)
128137
.header("ContentType", "application/x-www-form-urlencoded")
129138
.body(encoded)

azure_sdk_auth_aad/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@ pub mod errors;
2323
mod refresh_token;
2424
pub use refresh_token::*;
2525
mod naive_server;
26+
mod traits;
2627
pub use crate::device_code_flow::*;
2728
pub use crate::device_code_responses::*;
2829
use futures::TryFutureExt;
30+
mod responses;
2931
pub use naive_server::naive_server;
32+
mod prelude;
3033

3134
#[derive(Debug)]
3235
pub struct AuthObj {

azure_sdk_auth_aad/src/prelude.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub use crate::traits::*;

azure_sdk_auth_aad/src/refresh_token.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
use crate::responses::RefreshTokenResponse;
12
use azure_sdk_core::errors::AzureError;
23
use log::debug;
34
use oauth2::{AccessToken, ClientId, ClientSecret};
4-
use std::sync::Arc;
5+
use std::convert::TryInto;
56
use url::form_urlencoded;
67

78
pub async fn exchange_refresh_token(
@@ -10,7 +11,7 @@ pub async fn exchange_refresh_token(
1011
client_id: &ClientId,
1112
client_secret: Option<&ClientSecret>,
1213
refresh_token: &AccessToken,
13-
) -> Result<(), AzureError> {
14+
) -> Result<RefreshTokenResponse, AzureError> {
1415
let mut encoded = form_urlencoded::Serializer::new(String::new());
1516
let encoded = encoded.append_pair("grant_type", "refresh_token");
1617
let encoded = encoded.append_pair("client_id", client_id.as_str());
@@ -23,7 +24,7 @@ pub async fn exchange_refresh_token(
2324
let encoded = encoded.append_pair("refresh_token", refresh_token.secret());
2425
let encoded = encoded.finish();
2526

26-
println!("encoded ==> {}", encoded);
27+
debug!("encoded ==> {}", encoded);
2728

2829
let url = url::Url::parse(&format!(
2930
"https://login.microsoftonline.com/{}/oauth2/v2.0/token",
@@ -40,8 +41,7 @@ pub async fn exchange_refresh_token(
4041
.text()
4142
.await
4243
.map_err(|e| AzureError::GenericErrorWithText(e.to_string()))?;
44+
debug!("{}", ret);
4345

44-
println!("{}", ret);
45-
46-
Ok(())
46+
Ok(ret.try_into()?)
4747
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
mod refresh_token_response;
2+
pub use refresh_token_response::RefreshTokenResponse;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use crate::prelude::*;
2+
use oauth2::AccessToken;
3+
use std::convert::TryInto;
4+
5+
#[derive(Debug, Clone)]
6+
pub struct RefreshTokenResponse {
7+
token_type: String,
8+
scopes: Vec<String>,
9+
expires_in: u64,
10+
ext_expires_in: u64,
11+
access_token: AccessToken,
12+
refresh_token: AccessToken,
13+
}
14+
15+
impl TryInto<RefreshTokenResponse> for String {
16+
type Error = serde_json::Error;
17+
18+
fn try_into(self) -> Result<RefreshTokenResponse, Self::Error> {
19+
// we use a temp struct to deserialize the scope into
20+
// the scopes vec at later time
21+
#[derive(Debug, Clone, Deserialize)]
22+
pub struct _RefreshTokenResponse<'a> {
23+
token_type: String,
24+
scope: &'a str,
25+
expires_in: u64,
26+
ext_expires_in: u64,
27+
access_token: AccessToken,
28+
refresh_token: AccessToken,
29+
}
30+
31+
serde_json::from_str::<_RefreshTokenResponse>(&self).map(|rtr| RefreshTokenResponse {
32+
token_type: rtr.token_type,
33+
scopes: rtr.scope.split(' ').map(|s| s.to_owned()).collect(),
34+
expires_in: rtr.expires_in,
35+
ext_expires_in: rtr.ext_expires_in,
36+
access_token: rtr.access_token,
37+
refresh_token: rtr.refresh_token,
38+
})
39+
}
40+
}
41+
42+
impl BearerToken for RefreshTokenResponse {
43+
fn token_type(&self) -> &str {
44+
&self.token_type
45+
}
46+
fn scopes(&self) -> &[String] {
47+
&self.scopes
48+
}
49+
fn expires_in(&self) -> u64 {
50+
self.expires_in
51+
}
52+
fn access_token(&self) -> &AccessToken {
53+
&self.access_token
54+
}
55+
}
56+
57+
impl RefreshToken for RefreshTokenResponse {
58+
fn refresh_token(&self) -> &AccessToken {
59+
&self.refresh_token
60+
}
61+
}
62+
63+
impl ExtExpiresIn for RefreshTokenResponse {
64+
fn ext_expires_in(&self) -> u64 {
65+
self.ext_expires_in
66+
}
67+
}

azure_sdk_auth_aad/src/traits.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use oauth2::AccessToken;
2+
3+
pub trait BearerToken {
4+
fn token_type(&self) -> &str;
5+
fn scopes(&self) -> &[String];
6+
fn expires_in(&self) -> u64;
7+
fn access_token(&self) -> &AccessToken;
8+
}
9+
10+
pub trait RefreshToken {
11+
fn refresh_token(&self) -> &AccessToken;
12+
}
13+
14+
pub trait ExtExpiresIn {
15+
fn ext_expires_in(&self) -> u64;
16+
}

0 commit comments

Comments
 (0)