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
+ } ;
4
8
use base64:: { CharacterSet , Config } ;
5
- use chrono:: Utc ;
6
9
use openssl:: {
7
10
error:: ErrorStack ,
8
11
hash:: { hash, DigestBytes , MessageDigest } ,
@@ -14,6 +17,8 @@ use openssl::{
14
17
use serde:: Deserialize ;
15
18
use std:: str;
16
19
use std:: time:: Duration ;
20
+ use time:: OffsetDateTime ;
21
+ use url:: { form_urlencoded, Url } ;
17
22
18
23
/// Refresh time to use in seconds
19
24
const DEFAULT_REFRESH_TIME : i64 = 300 ;
@@ -130,16 +135,17 @@ struct AadTokenResponse {
130
135
access_token : String ,
131
136
}
132
137
133
- fn get_encoded_cert ( cert : & X509 ) -> Result < String , ClientCertificateCredentialError > {
138
+ fn get_encoded_cert ( cert : & X509 ) -> azure_core :: Result < String > {
134
139
Ok ( format ! (
135
140
"\" {}\" " ,
136
- base64:: encode(
137
- cert. to_pem( )
138
- . map_err( ClientCertificateCredentialError :: OpensslError ) ?
139
- )
141
+ base64:: encode( cert. to_pem( ) . map_err( |_| openssl_error( ) ) ?)
140
142
) )
141
143
}
142
144
145
+ fn openssl_error ( ) -> azure_core:: error:: Error {
146
+ Error :: message ( ErrorKind :: Credential , "Openssl decode error" )
147
+ }
148
+
143
149
#[ cfg_attr( target_arch = "wasm32" , async_trait:: async_trait( ?Send ) ) ]
144
150
#[ cfg_attr( not( target_arch = "wasm32" ) , async_trait:: async_trait) ]
145
151
impl TokenCredential for ClientCertificateCredential {
@@ -152,14 +158,14 @@ impl TokenCredential for ClientCertificateCredential {
152
158
) ;
153
159
154
160
let certificate = base64:: decode ( & self . client_certificate )
155
- . map_err ( ClientCertificateCredentialError :: DecodeError ) ?;
161
+ . map_err ( |_| Error :: message ( ErrorKind :: Credential , "Base64 decode failed" ) ) ?;
156
162
let certificate = Pkcs12 :: from_der ( & certificate)
157
- . map_err ( ClientCertificateCredentialError :: OpensslError ) ?
163
+ . map_err ( |_| openssl_error ( ) ) ?
158
164
. parse ( & self . client_certificate_pass )
159
- . map_err ( ClientCertificateCredentialError :: OpensslError ) ?;
165
+ . map_err ( |_| openssl_error ( ) ) ?;
160
166
161
167
let thumbprint = ClientCertificateCredential :: get_thumbprint ( & certificate. cert )
162
- . map_err ( ClientCertificateCredentialError :: OpensslError ) ?;
168
+ . map_err ( |_| openssl_error ( ) ) ?;
163
169
164
170
let uuid = uuid:: Uuid :: new_v4 ( ) ;
165
171
let current_time = OffsetDateTime :: now_utc ( ) . unix_timestamp ( ) ;
@@ -174,7 +180,7 @@ impl TokenCredential for ClientCertificateCredential {
174
180
let chain = chain
175
181
. into_iter ( )
176
182
. map ( |x| get_encoded_cert ( & x) )
177
- . collect :: < Result < Vec < String > , ClientCertificateCredentialError > > ( ) ?
183
+ . collect :: < Result < Vec < String > , azure_core :: error :: Error > > ( ) ?
178
184
. join ( "," ) ;
179
185
format ! { "{},{}" , base_signature, chain}
180
186
}
@@ -197,32 +203,42 @@ impl TokenCredential for ClientCertificateCredential {
197
203
198
204
let jwt = format ! ( "{}.{}" , header, payload) ;
199
205
let signature = ClientCertificateCredential :: sign ( & jwt, & certificate. pkey )
200
- . map_err ( ClientCertificateCredentialError :: OpensslError ) ?;
206
+ . map_err ( |_| openssl_error ( ) ) ?;
201
207
let sig = ClientCertificateCredential :: as_jwt_part ( & signature) ;
202
208
let client_assertion = format ! ( "{}.{}" , jwt, sig) ;
203
209
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) ;
214
231
215
232
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
+ }
225
240
241
+ let response: AadTokenResponse = serde_json:: from_slice ( & rsp_body) ?;
226
242
Ok ( TokenResponse :: new (
227
243
AccessToken :: new ( response. access_token . to_string ( ) ) ,
228
244
OffsetDateTime :: now_utc ( ) + Duration :: from_secs ( response. expires_in ) ,
0 commit comments