1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
use crate::oauth::wellknown::WellKnown;
use crate::oauth::{OAuth, OAuthError};
use from_as::*;
use std::convert::TryFrom;
use std::io::{Read, Write};
static LOGIN_LIVE_HOST: &str = "https://login.live.com";
static MICROSOFT_ONLINE_HOST: &str = "https://login.microsoftonline.com";
static OPEN_ID_PATH: &str = ".well-known/openid-configuration";
#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize, AsFile, FromFile)]
pub struct MicrosoftSigningKeysV1 {
pub issuer: String,
pub authorization_endpoint: String,
pub token_endpoint: String,
pub token_endpoint_auth_methods_supported: Vec<String>,
pub jwks_uri: String,
pub response_types_supported: Vec<String>,
pub response_modes_supported: Vec<String>,
pub subject_types_supported: Vec<String>,
pub scopes_supported: Vec<String>,
pub id_token_signing_alg_values_supported: Vec<String>,
pub claims_supported: Vec<String>,
pub request_uri_parameter_supported: bool,
pub end_session_endpoint: String,
pub frontchannel_logout_supported: bool,
pub http_logout_supported: bool,
}
#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize, AsFile, FromFile)]
pub struct MicrosoftSigningKeysV2 {
pub authorization_endpoint: String,
pub token_endpoint: String,
pub token_endpoint_auth_methods_supported: Vec<String>,
pub jwks_uri: String,
pub response_modes_supported: Vec<String>,
pub subject_types_supported: Vec<String>,
pub id_token_signing_alg_values_supported: Vec<String>,
pub http_logout_supported: bool,
pub frontchannel_logout_supported: bool,
pub end_session_endpoint: String,
pub response_types_supported: Vec<String>,
pub scopes_supported: Vec<String>,
pub issuer: String,
pub claims_supported: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub microsoft_multi_refresh_token: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub check_session_iframe: Option<String>,
pub userinfo_endpoint: String,
pub tenant_region_scope: Option<String>,
pub cloud_instance_name: String,
pub cloud_graph_host_name: String,
pub msgraph_host: String,
pub rbac_url: String,
}
pub enum GraphDiscovery {
V1,
V2,
Tenant(String),
}
impl GraphDiscovery {
pub fn url(&self) -> String {
match self {
GraphDiscovery::V1 => format!("{}/{}", LOGIN_LIVE_HOST, OPEN_ID_PATH),
GraphDiscovery::V2 => format!("{}/common/v2.0/{}", MICROSOFT_ONLINE_HOST, OPEN_ID_PATH),
GraphDiscovery::Tenant(tenant) => format!(
"{}/{}/v2.0/{}",
MICROSOFT_ONLINE_HOST, &tenant, OPEN_ID_PATH
),
}
}
pub fn signing_keys<T>(self) -> Result<T, OAuthError>
where
for<'de> T: serde::Deserialize<'de>,
{
let t: T = WellKnown::signing_keys(self.url().as_str())?;
Ok(t)
}
pub async fn async_signing_keys<T>(self) -> Result<T, OAuthError>
where
for<'de> T: serde::Deserialize<'de>,
{
let t: T = WellKnown::async_signing_keys(self.url().as_str()).await?;
Ok(t)
}
pub fn oauth(self) -> Result<OAuth, OAuthError> {
let mut oauth = OAuth::new();
match self {
GraphDiscovery::V1 => {
let k: MicrosoftSigningKeysV1 = self.signing_keys()?;
oauth
.authorize_url(k.authorization_endpoint.as_str())
.access_token_url(k.token_endpoint.as_str())
.refresh_token_url(k.token_endpoint.as_str())
.logout_url(k.end_session_endpoint.as_str());
Ok(oauth)
}
GraphDiscovery::V2 | GraphDiscovery::Tenant(_) => {
let k: MicrosoftSigningKeysV2 = self.signing_keys()?;
oauth
.authorize_url(k.authorization_endpoint.as_str())
.access_token_url(k.token_endpoint.as_str())
.refresh_token_url(k.token_endpoint.as_str())
.logout_url(k.end_session_endpoint.as_str());
Ok(oauth)
}
}
}
pub async fn async_oauth(self) -> Result<OAuth, OAuthError> {
let mut oauth = OAuth::new();
match self {
GraphDiscovery::V1 => {
let k: MicrosoftSigningKeysV1 = self.async_signing_keys().await?;
oauth
.authorize_url(k.authorization_endpoint.as_str())
.access_token_url(k.token_endpoint.as_str())
.refresh_token_url(k.token_endpoint.as_str())
.logout_url(k.end_session_endpoint.as_str());
Ok(oauth)
}
GraphDiscovery::V2 | GraphDiscovery::Tenant(_) => {
let k: MicrosoftSigningKeysV2 = self.async_signing_keys().await?;
oauth
.authorize_url(k.authorization_endpoint.as_str())
.access_token_url(k.token_endpoint.as_str())
.refresh_token_url(k.token_endpoint.as_str())
.logout_url(k.end_session_endpoint.as_str());
Ok(oauth)
}
}
}
}