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

update to std::future::Future #140

Closed
ctaggart opened this issue Sep 20, 2019 · 7 comments
Closed

update to std::future::Future #140

ctaggart opened this issue Sep 20, 2019 · 7 comments

Comments

@ctaggart
Copy link
Contributor

ctaggart commented Sep 20, 2019

https://seanmonstar.com/post/187493499882/hyper-alpha-supports-asyncawait
hyperium/hyper#1805

I'd love to be able to use the async/await syntax available. It will be in the stable release is
1.39 release which will be on November 7th. I'd like to be able to use it before then. std::future::Future is available in stable.

@MindFlavor
Copy link
Owner

Most of the async code is due to surfacing of hyper calls as you correctly pointed out. I would prefer to hold out changing the code until hyper stabilizes a bit though and then refactor everything (maybe dropping the combinators altogether!)

@skeet70
Copy link
Contributor

skeet70 commented Nov 4, 2019

Are you planning to support it once it drops in 1.39? I've been writing a prototype using async/await versions of warp and reqwest and trying to use azure_sdk_auth_aad but I'm running into loads of issues not knowing how all the compat works very well. Was hoping to PR a basic KMS lib to this repo if we end up going the Rust route here.

@MindFlavor
Copy link
Owner

Yes that's the plan. I do not have a clear timeline at the moment however.

I was wondering, however, why you are getting errors with azure_sdk_auth_aad. That crate is mostly sync. Can you elaborate what kind of error are you getting? Are you using authorize_non_interactive?

@skeet70
Copy link
Contributor

skeet70 commented Nov 5, 2019

I'm using authorize_non_interactive. Seems to be a problem with the main runtime being a current tokio one, which isn't able to run the future that comes back from the authorize function. When I tried to use tokio-core like the example did that caused issues with things not able to be shared across threads. When I asked for help figuring it out on the community discord they pointed me to the new tokio-compat crate.

@skeet70
Copy link
Contributor

skeet70 commented Nov 6, 2019

I wrote the auth using std futures and the technique that the official node kms library uses for auth without a tenant_id/tenant_domain. Haven't checked yet if it works the same way on other resources. This is very prototype.

#[derive(Deserialize, Serialize)]
pub struct AuthResponse {
    pub access_token: String,
}

pub async fn authorize(client: Arc<reqwest::Client>, client_id: &str, client_secret: &str, vault_base_url: &str, key_name: &str) -> Result<AuthResponse, reqwest::Error> {
    // bytes for the kms to wrap
    let empty_request = AzureWrapRequest {alg: super::AZURE_KEY_ENCRYPTION_METHOD.to_owned(), value: "".to_owned()};

    // we just fire off our request with no authorization intending to get back
    // a 401 that tells us where to point to authorize. If this works for other resources
    // then the authorize function could take an endpoint + empty request to make
    let wrap_url = format!(
        "{}/keys/{}//wrapkey?api-version=7.0",
        vault_base_url, key_name
    );
    let expected_401_response = client
        .post(&wrap_url)
        .json(&empty_request)
        .send()
        .await?;
    
    // parse out the `authorization` and `resource` from the bearer header
    // that came back on the 401
    let (authorization_url, resource) = parse_azure_bearer_header(
        expected_401_response
            .headers()
            .get("www-authenticate")
            .unwrap()
            .to_str()
            .unwrap(),
    )
    .unwrap();

    // post our credentials to the derived authorization url so we can get a token
    let params = [
        ("grant_type", "client_credentials"),
        ("client_id", client_id),
        ("client_secret", client_secret),
        ("resource", resource),
    ];
    let auth_url = format!("{}/oauth2/token", authorization_url);
    let auth_response = client
        .post(&auth_url)
        .form(&params)
        .send()
        .await?
        .json::<AuthResponse>()
        .await;

    auth_response
}

// pull out the authorization url we should hit, as well as the necessary resource.
fn parse_azure_bearer_header(bearer: &str) -> Option<(&str, &str)> {
    let re = regex::Regex::new(r#"authorization="([^"]+)".*resource="([^"]+)""#).unwrap();
    let captures = re.captures(bearer).unwrap();

    Some((
        captures.get(1).unwrap().as_str(),
        captures.get(2).unwrap().as_str(),
    ))
}

@MindFlavor
Copy link
Owner

I have created a PR that makes the AAD crate fully async (#162). Can you give it a try and tell me what do you think?

Thank you!

@skeet70
Copy link
Contributor

skeet70 commented Nov 8, 2019

It does work! Can't use it in my project yet because of a ring conflict with core but I tested it after removing my code that used that version of ring and it worked! I still can't find any documentation for the method that my above code and the azure node client use for authenticating when you don't have a tenant ID. Was hoping to find something so I could make it more generic and add it as an option.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants