azure_identity/client_credentials_flow/
mod.rs

1//! Authorize using the OAuth 2.0 client credentials flow
2//!
3//! For example:
4//!
5//! ```no_run
6//! use azure_identity::client_credentials_flow;
7//! use azure_core::Url;
8//!
9//! use std::env;
10//! use std::error::Error;
11//!
12//! #[tokio::main]
13//! async fn main() -> Result<(), Box<dyn Error>> {
14//!     let client_id =
15//!         env::var("CLIENT_ID").expect("Missing CLIENT_ID environment variable.");
16//!     let client_secret =
17//!         env::var("CLIENT_SECRET").expect("Missing CLIENT_SECRET environment variable.");
18//!     let tenant_id = env::var("TENANT_ID").expect("Missing TENANT_ID environment variable.");
19//!     let scope =
20//!         env::var("SCOPE").expect("Missing SCOPE environment variable.");
21//!
22//!     let http_client = azure_core::new_http_client();
23//!     // This will give you the final token to use in authorization.
24//!     let token = client_credentials_flow::perform(
25//!         http_client.clone(),
26//!         &client_id,
27//!         &client_secret,
28//!         &[&scope],
29//!         &tenant_id,
30//!     )
31//!     .await?;
32//!     Ok(())
33//! }
34//! ```
35//!
36//! You can learn more about this authorization flow [here](https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow).
37
38mod login_response;
39
40use azure_core::{
41    content_type,
42    error::{ErrorKind, ResultExt},
43    headers, HttpClient, Request, Url,
44};
45use azure_core::{from_json, Method};
46use login_response::LoginResponse;
47use std::sync::Arc;
48use url::form_urlencoded;
49
50/// Perform the client credentials flow
51pub async fn perform(
52    http_client: Arc<dyn HttpClient>,
53    client_id: &str,
54    client_secret: &str,
55    scopes: &[&str],
56    tenant_id: &str,
57) -> azure_core::Result<LoginResponse> {
58    let encoded: String = form_urlencoded::Serializer::new(String::new())
59        .append_pair("client_id", client_id)
60        .append_pair("scope", &scopes.join(" "))
61        .append_pair("client_secret", client_secret)
62        .append_pair("grant_type", "client_credentials")
63        .finish();
64
65    let url = Url::parse(&format!(
66        "https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
67    ))
68    .with_context(ErrorKind::DataConversion, || {
69        format!("The supplied tenant id could not be url encoded: {tenant_id}")
70    })?;
71
72    let mut req = Request::new(url, Method::Post);
73    req.insert_header(
74        headers::CONTENT_TYPE,
75        content_type::APPLICATION_X_WWW_FORM_URLENCODED,
76    );
77    req.set_body(encoded);
78    let rsp = http_client.execute_request(&req).await?;
79    let (rsp_status, rsp_headers, rsp_body) = rsp.deconstruct();
80    let rsp_body = rsp_body.collect().await?;
81    if !rsp_status.is_success() {
82        return Err(
83            ErrorKind::http_response_from_parts(rsp_status, &rsp_headers, &rsp_body).into_error(),
84        );
85    }
86    from_json(&rsp_body)
87}