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}