Skip to content

Commit 87d99e2

Browse files
authored
Fixing build break in using client_certificate credentials (#1018)
* Fixing build break in using client_certificate credentials * Fixing fmt error * Removing unused import * Minor feedback fixes * Adding full build test
1 parent 882e895 commit 87d99e2

File tree

3 files changed

+68
-39
lines changed

3 files changed

+68
-39
lines changed

.github/workflows/build.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ jobs:
6969
run: cargo check --all --features azurite_workaround
7070

7171
- name: sdk tests
72-
run: cargo test --all
72+
run: cargo test --all
7373

7474
- name: update readme of sdks
7575
run: |
@@ -92,6 +92,19 @@ jobs:
9292
- name: check `into_future` feature
9393
run: cargo check --all --features into_future
9494

95+
nightly-build:
96+
name: Build Nightly
97+
runs-on: ubuntu-20.04
98+
steps:
99+
- uses: actions/checkout@v2
100+
- uses: actions-rs/toolchain@v1
101+
with:
102+
toolchain: nightly
103+
profile: minimal
104+
override: true
105+
- name: Build Nightly
106+
run: cargo +nightly build --all-features
107+
95108
test-services:
96109
name: Services Tests
97110
runs-on: ubuntu-20.04

sdk/identity/examples/client_certificate_credentials.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ async fn get_certficate(
1919
certificate_name: &str,
2020
) -> Result<Vec<u8>, Box<dyn Error>> {
2121
let creds = DefaultAzureCredential::default();
22-
let mut client = KeyvaultClient::new(
22+
let client = KeyvaultClient::new(
2323
format!("https://{}.vault.azure.net", vault_name).as_str(),
2424
std::sync::Arc::new(creds),
2525
)?
26-
.secret_client(certificate_name);
27-
let secret = client.get().into_future().await?;
28-
let cert = base64::decode(secret.value())?;
26+
.secret_client();
27+
let secret = client.get(certificate_name).into_future().await?;
28+
let cert = base64::decode(secret.value)?;
2929
Ok(cert)
3030
}
3131

sdk/identity/src/token_credentials/client_certificate_credentials.rs

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
use super::{authority_hosts, TokenCredential};
2-
use azure_core::auth::{AccessToken, TokenResponse};
3-
use azure_core::error::{ErrorKind, ResultExt};
1+
use super::authority_hosts;
2+
use azure_core::{
3+
auth::{AccessToken, TokenCredential, TokenResponse},
4+
content_type,
5+
error::{Error, ErrorKind},
6+
headers, new_http_client, Method, Request,
7+
};
48
use base64::{CharacterSet, Config};
5-
use chrono::Utc;
69
use openssl::{
710
error::ErrorStack,
811
hash::{hash, DigestBytes, MessageDigest},
@@ -14,6 +17,8 @@ use openssl::{
1417
use serde::Deserialize;
1518
use std::str;
1619
use std::time::Duration;
20+
use time::OffsetDateTime;
21+
use url::{form_urlencoded, Url};
1722

1823
/// Refresh time to use in seconds
1924
const DEFAULT_REFRESH_TIME: i64 = 300;
@@ -130,16 +135,17 @@ struct AadTokenResponse {
130135
access_token: String,
131136
}
132137

133-
fn get_encoded_cert(cert: &X509) -> Result<String, ClientCertificateCredentialError> {
138+
fn get_encoded_cert(cert: &X509) -> azure_core::Result<String> {
134139
Ok(format!(
135140
"\"{}\"",
136-
base64::encode(
137-
cert.to_pem()
138-
.map_err(ClientCertificateCredentialError::OpensslError)?
139-
)
141+
base64::encode(cert.to_pem().map_err(|_| openssl_error())?)
140142
))
141143
}
142144

145+
fn openssl_error() -> azure_core::error::Error {
146+
Error::message(ErrorKind::Credential, "Openssl decode error")
147+
}
148+
143149
#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
144150
#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
145151
impl TokenCredential for ClientCertificateCredential {
@@ -152,14 +158,14 @@ impl TokenCredential for ClientCertificateCredential {
152158
);
153159

154160
let certificate = base64::decode(&self.client_certificate)
155-
.map_err(ClientCertificateCredentialError::DecodeError)?;
161+
.map_err(|_| Error::message(ErrorKind::Credential, "Base64 decode failed"))?;
156162
let certificate = Pkcs12::from_der(&certificate)
157-
.map_err(ClientCertificateCredentialError::OpensslError)?
163+
.map_err(|_| openssl_error())?
158164
.parse(&self.client_certificate_pass)
159-
.map_err(ClientCertificateCredentialError::OpensslError)?;
165+
.map_err(|_| openssl_error())?;
160166

161167
let thumbprint = ClientCertificateCredential::get_thumbprint(&certificate.cert)
162-
.map_err(ClientCertificateCredentialError::OpensslError)?;
168+
.map_err(|_| openssl_error())?;
163169

164170
let uuid = uuid::Uuid::new_v4();
165171
let current_time = OffsetDateTime::now_utc().unix_timestamp();
@@ -174,7 +180,7 @@ impl TokenCredential for ClientCertificateCredential {
174180
let chain = chain
175181
.into_iter()
176182
.map(|x| get_encoded_cert(&x))
177-
.collect::<Result<Vec<String>, ClientCertificateCredentialError>>()?
183+
.collect::<Result<Vec<String>, azure_core::error::Error>>()?
178184
.join(",");
179185
format! {"{},{}", base_signature, chain}
180186
}
@@ -197,32 +203,42 @@ impl TokenCredential for ClientCertificateCredential {
197203

198204
let jwt = format!("{}.{}", header, payload);
199205
let signature = ClientCertificateCredential::sign(&jwt, &certificate.pkey)
200-
.map_err(ClientCertificateCredentialError::OpensslError)?;
206+
.map_err(|_| openssl_error())?;
201207
let sig = ClientCertificateCredential::as_jwt_part(&signature);
202208
let client_assertion = format!("{}.{}", jwt, sig);
203209

204-
let form_data = vec![
205-
("client_id", self.client_id.to_owned()),
206-
("scope", format!("{}/.default", resource)),
207-
(
208-
"client_assertion_type",
209-
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer".to_owned(),
210-
),
211-
("client_assertion", client_assertion),
212-
("grant_type", "client_credentials".to_owned()),
213-
];
210+
let encoded = {
211+
let mut encoded = &mut form_urlencoded::Serializer::new(String::new());
212+
encoded = encoded
213+
.append_pair("client_id", self.client_id.as_str())
214+
.append_pair("scope", format!("{}/.default", resource).as_str())
215+
.append_pair(
216+
"client_assertion_type",
217+
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
218+
)
219+
.append_pair("client_assertion", client_assertion.as_str())
220+
.append_pair("grant_type", "client_credentials");
221+
encoded.finish()
222+
};
223+
224+
let url = Url::parse(url)?;
225+
let mut req = Request::new(url, Method::Post);
226+
req.insert_header(
227+
headers::CONTENT_TYPE,
228+
content_type::APPLICATION_X_WWW_FORM_URLENCODED,
229+
);
230+
req.set_body(encoded);
214231

215232
let http_client = new_http_client();
216-
let response: AadTokenResponse = client
217-
.post(url)
218-
.form(&form_data)
219-
.send()
220-
.await
221-
.map_err(ClientCertificateCredentialError::ReqwestError)?
222-
.json()
223-
.await
224-
.map_err(ClientCertificateCredentialError::ReqwestError)?;
233+
let rsp = http_client.execute_request(&req).await?;
234+
let rsp_status = rsp.status();
235+
let rsp_body = rsp.into_body().collect().await?;
236+
237+
if !rsp_status.is_success() {
238+
return Err(ErrorKind::http_response_from_body(rsp_status, &rsp_body).into_error());
239+
}
225240

241+
let response: AadTokenResponse = serde_json::from_slice(&rsp_body)?;
226242
Ok(TokenResponse::new(
227243
AccessToken::new(response.access_token.to_string()),
228244
OffsetDateTime::now_utc() + Duration::from_secs(response.expires_in),

0 commit comments

Comments
 (0)