use std::collections::BTreeSet;
use oauth2::{EmptyExtraTokenFields, StandardTokenResponse, basic::BasicTokenType};
use crate::configuration::{oidc::DISCOVERY_REQUIRED_SCOPE, settings::PREFERRED_LOGIN_SCOPES};
pub(crate) type LoginResponse = StandardTokenResponse<EmptyExtraTokenFields, BasicTokenType>;
pub(crate) fn oauth_http_client() -> Result<oauth2::reqwest::Client, oauth2::reqwest::Error> {
oauth2::reqwest::ClientBuilder::new()
.redirect(oauth2::reqwest::redirect::Policy::none())
.build()
}
pub fn resolve_scopes(
configured_scopes: Option<BTreeSet<String>>,
advertised_scopes: Option<BTreeSet<String>>,
) -> BTreeSet<String> {
let mut scopes = configured_scopes
.or_else(|| {
advertised_scopes.map(|advertised_scopes| {
advertised_scopes
.into_iter()
.filter(|scope| PREFERRED_LOGIN_SCOPES.contains(&scope.as_str()))
.collect()
})
})
.unwrap_or_default();
scopes.insert(DISCOVERY_REQUIRED_SCOPE.to_string());
scopes
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
macro_rules! set {
($($v:expr),* $(,)?) => {
[$($v,)*]
.iter()
.map(|s| s.to_string())
.collect::<BTreeSet<_>>()
};
}
pub(crate) fn default_scope_string() -> String {
resolve_scopes(None, None)
.into_iter()
.collect::<Vec<_>>()
.join(" ")
}
#[test]
fn test_configured_scopes_are_used_verbatim() {
let configured = set!["custom:read", "custom:write"];
let expected = set!["custom:read", "custom:write", DISCOVERY_REQUIRED_SCOPE];
let actual = resolve_scopes(Some(configured), None);
assert_eq!(expected, actual);
}
#[test]
fn test_unadvertised_scopes_fall_back_to_all_defaults() {
let actual = resolve_scopes(None, None);
let expected = set![DISCOVERY_REQUIRED_SCOPE];
assert_eq!(expected, actual);
}
#[test]
fn test_defaults_are_narrowed_to_advertised_scopes() {
let cognito_scopes = set![
DISCOVERY_REQUIRED_SCOPE,
"email",
"profile",
"something_else"
];
let expected = set![DISCOVERY_REQUIRED_SCOPE, "email", "profile"];
let actual = resolve_scopes(None, Some(cognito_scopes.clone()));
assert_eq!(expected, actual);
}
#[test]
fn test_configured_scopes_are_not_narrowed() {
let configured = set!["offline_access", "something_else"];
let advertised = set!["email"];
let expected = set![DISCOVERY_REQUIRED_SCOPE, "offline_access", "something_else"];
let actual = resolve_scopes(Some(configured), Some(advertised));
assert_eq!(expected, actual);
}
}