graph_oauth/identity/
authority.rs

1use std::fmt::Display;
2use url::{ParseError, Url};
3
4lazy_static! {
5    pub static ref AZURE_PUBLIC_CLOUD_INSTANCE: Url = {
6        Url::parse(AzureCloudInstance::AzurePublic.as_ref())
7            .expect("Unable to create Azure Public Cloud Instance Url")
8    };
9    pub static ref AZURE_CHINA_CLOUD_INSTANCE: Url = {
10        Url::parse(AzureCloudInstance::AzureChina.as_ref())
11            .expect("Unable to create Azure China Cloud Instance Url")
12    };
13    pub static ref AZURE_GERMANY_CLOUD_INSTANCE: Url = {
14        Url::parse(AzureCloudInstance::AzureGermany.as_ref())
15            .expect("Unable to create Azure Germany Cloud Instance Url")
16    };
17    pub static ref AZURE_US_GOVERNMENT: Url = {
18        Url::parse(AzureCloudInstance::AzureUsGovernment.as_ref())
19            .expect("Unable to create Azure Us Government Cloud Instance Url")
20    };
21}
22
23/// STS instance (for instance https://login.microsoftonline.com for the Azure public cloud).
24/// Maps to the instance url string.
25#[derive(
26    Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize,
27)]
28pub enum AzureCloudInstance {
29    /// Microsoft Azure public cloud. Maps to https://login.microsoftonline.com
30    #[default]
31    AzurePublic,
32    /// Microsoft Chinese national cloud. Maps to https://login.chinacloudapi.cn
33    AzureChina,
34    /// Microsoft German national cloud ("Black Forest"). Maps to https://login.microsoftonline.de
35    AzureGermany,
36    /// US Government cloud. Maps to https://login.microsoftonline.us
37    AzureUsGovernment,
38}
39
40impl AzureCloudInstance {
41    pub fn get_open_id_configuration_url(&self, authority: Authority) -> String {
42        format!("{}/v2.0/{}", self.as_ref(), authority.as_ref())
43    }
44}
45
46impl AsRef<str> for AzureCloudInstance {
47    fn as_ref(&self) -> &str {
48        match self {
49            AzureCloudInstance::AzurePublic => "https://login.microsoftonline.com",
50            AzureCloudInstance::AzureChina => "https://login.chinacloudapi.cn",
51            AzureCloudInstance::AzureGermany => "https://login.microsoftonline.de",
52            AzureCloudInstance::AzureUsGovernment => "https://login.microsoftonline.us",
53        }
54    }
55}
56
57impl From<&AzureCloudInstance> for Url {
58    fn from(value: &AzureCloudInstance) -> Self {
59        match value {
60            AzureCloudInstance::AzurePublic => AZURE_PUBLIC_CLOUD_INSTANCE.clone(),
61            AzureCloudInstance::AzureChina => AZURE_CHINA_CLOUD_INSTANCE.clone(),
62            AzureCloudInstance::AzureGermany => AZURE_GERMANY_CLOUD_INSTANCE.clone(),
63            AzureCloudInstance::AzureUsGovernment => AZURE_US_GOVERNMENT.clone(),
64        }
65    }
66}
67
68impl From<AzureCloudInstance> for Url {
69    fn from(value: AzureCloudInstance) -> Self {
70        match value {
71            AzureCloudInstance::AzurePublic => AZURE_PUBLIC_CLOUD_INSTANCE.clone(),
72            AzureCloudInstance::AzureChina => AZURE_CHINA_CLOUD_INSTANCE.clone(),
73            AzureCloudInstance::AzureGermany => AZURE_GERMANY_CLOUD_INSTANCE.clone(),
74            AzureCloudInstance::AzureUsGovernment => AZURE_US_GOVERNMENT.clone(),
75        }
76    }
77}
78
79impl AzureCloudInstance {
80    pub fn auth_uri(&self, authority: &Authority) -> Result<Url, ParseError> {
81        Url::parse(&format!(
82            "{}/{}/oauth2/v2.0/authorize",
83            self.as_ref(),
84            authority.as_ref()
85        ))
86    }
87
88    pub fn token_uri(&self, authority: &Authority) -> Result<Url, ParseError> {
89        Url::parse(&format!(
90            "{}/{}/oauth2/v2.0/token",
91            self.as_ref(),
92            authority.as_ref()
93        ))
94    }
95
96    pub fn admin_consent_uri(&self, authority: &Authority) -> Result<Url, ParseError> {
97        Url::parse(&format!(
98            "{}/{}/adminconsent",
99            self.as_ref(),
100            authority.as_ref()
101        ))
102    }
103
104    pub fn device_code_uri(&self, authority: &Authority) -> Result<Url, ParseError> {
105        Url::parse(&format!(
106            "{}/{}/oauth2/v2.0/devicecode",
107            self.as_ref(),
108            authority.as_ref()
109        ))
110    }
111
112    pub fn openid_configuration_uri(&self, authority: &Authority) -> Result<Url, ParseError> {
113        Url::parse(&format!(
114            "{}/{}/v2.0/.well-known/openid-configuration",
115            self.as_ref(),
116            authority.as_ref()
117        ))
118    }
119
120    pub fn issuer(&self, authority: &Authority) -> Result<Url, ParseError> {
121        Url::parse(&format!("{}/{}/v2.0", self.as_ref(), authority.as_ref()))
122    }
123
124    /*
125    pub fn default_microsoft_graph_scope(&self) -> &'static str {
126        "https://graph.microsoft.com/.default"
127    }
128
129    pub fn default_managed_identity_scope(&self) -> &'static str {
130        match self {
131            AzureCloudInstance::AzurePublic => "https://management.azure.com//.default",
132            AzureCloudInstance::AzureChina => "https://management.chinacloudapi.cn/.default",
133            AzureCloudInstance::AzureGermany => "https://management.microsoftazure.de/.default",
134            AzureCloudInstance::AzureUsGovernment => {
135                "https://management.usgovcloudapi.net/.default"
136            }
137        }
138    }
139    */
140}
141
142/// Specifies which Microsoft accounts can be used for sign-in with a given application.
143/// See https://aka.ms/msal-net-application-configuration
144///
145/// [AadAuthorityAudience] uses the application names selected in the Azure Portal and
146/// maps to [Authority]
147#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
148pub enum AadAuthorityAudience {
149    /// Users with a Microsoft work or school account in my organization’s Azure AD tenant (i.e. single tenant).
150    /// Maps to https://[AzureCloudInstance]/[AadAuthorityAudience::AzureAdMyOrg(tenant_id)]
151    /// or https://[instance]/[tenant_id]
152    ///
153    /// # Using Tenant Id
154    /// ```rust
155    /// use graph_oauth::AadAuthorityAudience;
156    /// let authority_audience = AadAuthorityAudience::AzureAdMyOrg("tenant_id".into());
157    /// ```
158    AzureAdMyOrg(String),
159
160    /// Users with a personal Microsoft account, or a work or school account in any organization’s Azure AD tenant
161    /// Maps to https://[AzureCloudInstance]/common/ or https://[instance]/[common]/\
162    #[default]
163    AzureAdAndPersonalMicrosoftAccount,
164
165    /// Users with a Microsoft work or school account in any organization’s Azure AD tenant (i.e. multi-tenant).
166    /// Maps to https://[AzureCloudInstance]/organizations/ or https://[instance]/organizations/
167    AzureAdMultipleOrgs,
168
169    /// Users with a personal Microsoft account. Maps to https://[AzureCloudInstance]/consumers/
170    /// or https://[instance]/consumers/
171    PersonalMicrosoftAccount,
172}
173
174impl AadAuthorityAudience {
175    pub fn as_str(&self) -> &str {
176        match self {
177            AadAuthorityAudience::AzureAdMyOrg(tenant) => tenant.as_str(),
178            AadAuthorityAudience::AzureAdAndPersonalMicrosoftAccount => "common",
179            AadAuthorityAudience::AzureAdMultipleOrgs => "organizations",
180            AadAuthorityAudience::PersonalMicrosoftAccount => "consumers",
181        }
182    }
183}
184
185impl AsRef<str> for AadAuthorityAudience {
186    fn as_ref(&self) -> &str {
187        self.as_str()
188    }
189}
190
191impl Display for AadAuthorityAudience {
192    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193        write!(f, "{}", self.as_str())
194    }
195}
196
197impl From<&str> for AadAuthorityAudience {
198    fn from(value: &str) -> Self {
199        match value.as_bytes() {
200            b"common" => AadAuthorityAudience::AzureAdAndPersonalMicrosoftAccount,
201            b"organizations" => AadAuthorityAudience::AzureAdMultipleOrgs,
202            b"consumers" => AadAuthorityAudience::PersonalMicrosoftAccount,
203            _ => AadAuthorityAudience::AzureAdMyOrg(value.to_string()),
204        }
205    }
206}
207
208/// Specifies which Microsoft accounts can be used for sign-in with a given application.
209/// See https://aka.ms/msal-net-application-configuration
210#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
211pub enum Authority {
212    /// Users with both a personal Microsoft account and a work or school account
213    /// from Azure AD can sign in to the application.
214    /// /// Maps to https://[AzureCloudInstance]/common/
215    ///
216    /// [Authority::AzureActiveDirectory] is the same as [Authority::Common].
217    /// [Authority::Common] is a convenience enum variant that may be more
218    /// familiar with it from the Microsoft Identity Platform documentation.
219    #[default]
220    AzureActiveDirectory,
221    AzureDirectoryFederatedServices,
222    /// Users with both a personal Microsoft account and a work or school account
223    /// from Azure AD can sign in to the application.
224    /// Maps to https://[instance]/common/
225    ///
226    /// [Authority::Common] is the same as [Authority::AzureActiveDirectory].
227    ///
228    /// [Authority::Common] is a convenience enum variant that may be more
229    /// familiar with it from the Microsoft Identity Platform documentation.
230    Common,
231    /// Only users with work or school accounts from Azure AD can sign in to the application.
232    Organizations,
233    /// Only users with a personal Microsoft account can sign in to the application.
234    Consumers,
235    /// The value can be the domain name of the Azure AD tenant or the tenant ID in GUID format.
236    /// You can also use the consumer tenant GUID, 9188040d-6c67-4c5b-b112-36a304b66dad,
237    /// in place of consumers.
238    ///
239    /// Only users from a specific Azure AD tenant (directory members with a work or
240    /// school account or directory guests with a personal Microsoft account) can sign in
241    /// to the application.
242    TenantId(String),
243}
244
245impl Authority {
246    pub fn tenant_id(&self) -> Option<&String> {
247        match self {
248            Authority::TenantId(tenant_id) => Some(tenant_id),
249            _ => None,
250        }
251    }
252
253    pub fn as_str(&self) -> &str {
254        match self {
255            Authority::AzureActiveDirectory | Authority::Common => "common",
256            Authority::AzureDirectoryFederatedServices => "adfs",
257            Authority::Organizations => "organizations",
258            Authority::Consumers => "consumers",
259            Authority::TenantId(tenant_id) => tenant_id.as_str(),
260        }
261    }
262}
263
264impl From<&AadAuthorityAudience> for Authority {
265    fn from(value: &AadAuthorityAudience) -> Self {
266        match value {
267            AadAuthorityAudience::AzureAdAndPersonalMicrosoftAccount => Authority::Common,
268            AadAuthorityAudience::AzureAdMyOrg(tenant_id) => Authority::TenantId(tenant_id.clone()),
269            AadAuthorityAudience::AzureAdMultipleOrgs => Authority::Organizations,
270            AadAuthorityAudience::PersonalMicrosoftAccount => Authority::Consumers,
271        }
272    }
273}
274
275impl From<AadAuthorityAudience> for Authority {
276    fn from(value: AadAuthorityAudience) -> Self {
277        match value {
278            AadAuthorityAudience::AzureAdAndPersonalMicrosoftAccount => Authority::Common,
279            AadAuthorityAudience::AzureAdMyOrg(tenant_id) => Authority::TenantId(tenant_id),
280            AadAuthorityAudience::AzureAdMultipleOrgs => Authority::Organizations,
281            AadAuthorityAudience::PersonalMicrosoftAccount => Authority::Consumers,
282        }
283    }
284}
285
286impl AsRef<str> for Authority {
287    fn as_ref(&self) -> &str {
288        self.as_str()
289    }
290}
291
292impl Display for Authority {
293    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
294        write!(f, "{}", self.as_str())
295    }
296}
297
298impl From<&str> for Authority {
299    fn from(value: &str) -> Self {
300        match value.as_bytes() {
301            b"aad" => Authority::AzureActiveDirectory,
302            b"common" => Authority::Common,
303            b"adfs" => Authority::AzureDirectoryFederatedServices,
304            b"organizations" => Authority::Organizations,
305            b"consumers" => Authority::Consumers,
306            _ => Authority::TenantId(value.to_owned()),
307        }
308    }
309}