Add authentication from upstream providers #107

Merged
pierre merged 25 commits from feat-upstream-providers into master 2023-04-27 10:10:29 +00:00
5 changed files with 63 additions and 37 deletions
Showing only changes of commit bf20e5ad13 - Show all commits

View File

@ -20,8 +20,7 @@ use crate::data::code_challenge::CodeChallenge;
use crate::data::current_user::CurrentUser; use crate::data::current_user::CurrentUser;
use crate::data::id_token::IdToken; use crate::data::id_token::IdToken;
use crate::data::jwt_signer::{JWTSigner, JsonWebKey}; use crate::data::jwt_signer::{JWTSigner, JsonWebKey};
use crate::data::open_id_user_info::OpenIDUserInfo; use crate::data::openid_primitive::{OpenIDConfig, OpenIDUserInfo, TokenResponse};
use crate::data::openid_primitive::{OpenIDConfig, TokenResponse};
use crate::data::session_identity::SessionIdentity; use crate::data::session_identity::SessionIdentity;
use crate::data::user::User; use crate::data::user::User;
use crate::utils::string_utils::rand_str; use crate::utils::string_utils::rand_str;
@ -627,12 +626,12 @@ async fn user_info(
}; };
HttpResponse::Ok().json(OpenIDUserInfo { HttpResponse::Ok().json(OpenIDUserInfo {
name: user.full_name(), name: Some(user.full_name()),
sub: user.uid.0, sub: user.uid.0,
given_name: user.first_name, given_name: Some(user.first_name),
family_name: user.last_name, family_name: Some(user.last_name),
preferred_username: user.username, preferred_username: Some(user.username),
email: user.email, email: Some(user.email),
email_verified: true, email_verified: Some(true),
}) })
} }

View File

@ -225,9 +225,10 @@ pub async fn finish_login(
} }
}; };
println!("go on {:?}", token); // Use access token to get user information
let info = provider_config.get_userinfo(&token).await;
println!("info: {:?}", info);
// TODO : check token signature
// TODO : check if user is authorized to access application // TODO : check if user is authorized to access application
// TODO : check if 2FA is enabled // TODO : check if 2FA is enabled
// TODO : redirect user to login route // TODO : redirect user to login route

View File

@ -1,24 +1 @@
/// Refer to <https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims> for more information
#[derive(Debug, serde::Serialize)]
pub struct OpenIDUserInfo {
/// Subject - Identifier for the End-User at the Issuer
pub sub: String,
/// End-User's full name in displayable form including all name parts, possibly including titles and suffixes, ordered according to the End-User's locale and preferences.
pub name: String,
/// Given name(s) or first name(s) of the End-User. Note that in some cultures, people can have multiple given names; all can be present, with the names being separated by space characters.
pub given_name: String,
/// Surname(s) or last name(s) of the End-User. Note that in some cultures, people can have multiple family names or no family name; all can be present, with the names being separated by space characters.
pub family_name: String,
/// Shorthand name by which the End-User wishes to be referred to at the RP, such as janedoe or j.doe. This value MAY be any valid JSON string including special characters such as @, /, or whitespace. The RP MUST NOT rely upon this value being unique, as discussed in
pub preferred_username: String,
/// End-User's preferred e-mail address. Its value MUST conform to the RFC 5322 RFC5322 addr-spec syntax. The RP MUST NOT rely upon this value being unique, as discussed in Section 5.7.
pub email: String,
/// True if the End-User's e-mail address has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this e-mail address was controlled by the End-User at the time the verification was performed. The means by which an e-mail address is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating.
pub email_verified: bool,
}

View File

@ -72,3 +72,36 @@ pub struct TokenResponse {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub id_token: Option<String>, pub id_token: Option<String>,
} }
/// Refer to <https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims> for more information
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct OpenIDUserInfo {
/// Subject - Identifier for the End-User at the Issuer
///
/// This is the only mandatory field
pub sub: String,
/// End-User's full name in displayable form including all name parts, possibly including titles and suffixes, ordered according to the End-User's locale and preferences.
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
/// Given name(s) or first name(s) of the End-User. Note that in some cultures, people can have multiple given names; all can be present, with the names being separated by space characters.
#[serde(skip_serializing_if = "Option::is_none")]
pub given_name: Option<String>,
/// Surname(s) or last name(s) of the End-User. Note that in some cultures, people can have multiple family names or no family name; all can be present, with the names being separated by space characters.
#[serde(skip_serializing_if = "Option::is_none")]
pub family_name: Option<String>,
/// Shorthand name by which the End-User wishes to be referred to at the RP, such as janedoe or j.doe. This value MAY be any valid JSON string including special characters such as @, /, or whitespace. The RP MUST NOT rely upon this value being unique, as discussed in
#[serde(skip_serializing_if = "Option::is_none")]
pub preferred_username: Option<String>,
/// End-User's preferred e-mail address. Its value MUST conform to the RFC 5322 RFC5322 addr-spec syntax. The RP MUST NOT rely upon this value being unique, as discussed in Section 5.7.
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
/// True if the End-User's e-mail address has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this e-mail address was controlled by the End-User at the time the verification was performed. The means by which an e-mail address is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating.
#[serde(skip_serializing_if = "Option::is_none")]
pub email_verified: Option<bool>,
}

View File

@ -8,7 +8,7 @@ use crate::actors::providers_states_actor::ProviderLoginState;
use crate::constants::OIDC_PROVIDERS_LIFETIME; use crate::constants::OIDC_PROVIDERS_LIFETIME;
use crate::data::app_config::AppConfig; use crate::data::app_config::AppConfig;
use crate::data::jwt_signer::JsonWebKey; use crate::data::jwt_signer::JsonWebKey;
use crate::data::openid_primitive::TokenResponse; use crate::data::openid_primitive::{OpenIDUserInfo, TokenResponse};
use crate::data::provider::Provider; use crate::data::provider::Provider;
use crate::utils::err::Res; use crate::utils::err::Res;
use crate::utils::time::time; use crate::utils::time::time;
@ -32,7 +32,7 @@ pub struct ProviderJWKs {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ProviderConfiguration { pub struct ProviderConfiguration {
pub discovery: ProviderDiscovery, pub discovery: ProviderDiscovery,
pub keys: ProviderJWKs, //pub keys: ProviderJWKs,
pub expire: u64, pub expire: u64,
} }
@ -72,6 +72,22 @@ impl ProviderConfiguration {
.json() .json()
.await?) .await?)
} }
/// Retrieve information about the user, using given [TokenResponse]
pub async fn get_userinfo(&self, token: &TokenResponse) -> Res<OpenIDUserInfo> {
Ok(reqwest::Client::new()
.get(
self.discovery
.userinfo_endpoint
.as_ref()
.expect("Userinfo endpoint is required by this implementation!"),
)
.header("Authorization", format!("Bearer {}", token.access_token))
.send()
.await?
.json()
.await?)
}
} }
thread_local! { thread_local! {
@ -108,11 +124,11 @@ impl ProviderConfigurationHelper {
.json() .json()
.await?; .await?;
let keys: ProviderJWKs = reqwest::get(&discovery.jwks_uri).await?.json().await?; // let keys: ProviderJWKs = reqwest::get(&discovery.jwks_uri).await?.json().await?;
Ok(ProviderConfiguration { Ok(ProviderConfiguration {
discovery, discovery,
keys, // keys,
expire: time() + OIDC_PROVIDERS_LIFETIME, expire: time() + OIDC_PROVIDERS_LIFETIME,
}) })
} }