azure_identity/
authorization_code_flow.rs
1use crate::oauth2_http_client::Oauth2HttpClient;
6use azure_core::{
7 error::{ErrorKind, ResultExt},
8 HttpClient, Url,
9};
10use oauth2::{basic::BasicClient, Scope};
11use oauth2::{ClientId, ClientSecret};
12use std::sync::Arc;
13
14pub fn start(
19 client_id: ClientId,
20 client_secret: Option<ClientSecret>,
21 tenant_id: &str,
22 redirect_url: Url,
23 scopes: &[&str],
24) -> AuthorizationCodeFlow {
25 let auth_url = oauth2::AuthUrl::from_url(
26 Url::parse(&format!(
27 "https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/authorize"
28 ))
29 .expect("Invalid authorization endpoint URL"),
30 );
31 let token_url = oauth2::TokenUrl::from_url(
32 Url::parse(&format!(
33 "https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
34 ))
35 .expect("Invalid token endpoint URL"),
36 );
37
38 let client = BasicClient::new(client_id, client_secret, auth_url, Some(token_url))
40 .set_auth_type(oauth2::AuthType::RequestBody)
43 .set_redirect_uri(oauth2::RedirectUrl::from_url(redirect_url));
44
45 let (pkce_code_challenge, pkce_code_verifier) = oauth2::PkceCodeChallenge::new_random_sha256();
48
49 let scopes = scopes.iter().map(ToString::to_string).map(Scope::new);
50
51 let (authorize_url, csrf_state) = client
53 .authorize_url(oauth2::CsrfToken::new_random)
54 .add_scopes(scopes)
55 .set_pkce_challenge(pkce_code_challenge)
56 .url();
57
58 AuthorizationCodeFlow {
59 client,
60 authorize_url,
61 csrf_state,
62 pkce_code_verifier,
63 }
64}
65
66#[derive(Debug)]
68pub struct AuthorizationCodeFlow {
69 pub client: BasicClient,
71 pub authorize_url: Url,
73 pub csrf_state: oauth2::CsrfToken,
75 pub pkce_code_verifier: oauth2::PkceCodeVerifier,
77}
78
79impl AuthorizationCodeFlow {
80 pub async fn exchange(
82 self,
83 http_client: Arc<dyn HttpClient>,
84 code: oauth2::AuthorizationCode,
85 ) -> azure_core::Result<
86 oauth2::StandardTokenResponse<oauth2::EmptyExtraTokenFields, oauth2::basic::BasicTokenType>,
87 > {
88 let oauth_http_client = Oauth2HttpClient::new(http_client.clone());
89 self.client
90 .exchange_code(code)
91 .set_pkce_verifier(self.pkce_code_verifier)
93 .request_async(|request| oauth_http_client.request(request))
94 .await
95 .context(
96 ErrorKind::Credential,
97 "exchanging an authorization code for a token failed",
98 )
99 }
100}