mirror of
https://gitlab.com/famedly/conduit.git
synced 2025-07-02 16:38:36 +00:00
fix
This commit is contained in:
parent
10ce7ea3a9
commit
b80141b33b
8 changed files with 255 additions and 599 deletions
|
@ -1,50 +1,36 @@
|
|||
mod data;
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
collections::{HashMap, HashSet},
|
||||
collections::HashSet,
|
||||
hash::{Hash, Hasher},
|
||||
str::FromStr,
|
||||
sync::{Arc, RwLock},
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
api::client_server::TOKEN_LENGTH,
|
||||
config::{sso::ProviderConfig as Config, IdpConfig},
|
||||
api::client_server::{LOGIN_TOKEN_EXPIRATION_SECS, TOKEN_LENGTH},
|
||||
config::IdpConfig,
|
||||
utils, Error, Result,
|
||||
};
|
||||
pub use data::Data;
|
||||
use email_address::EmailAddress;
|
||||
use futures_util::future::{self};
|
||||
use mas_oidc_client::{
|
||||
http_service::{hyper, HttpService},
|
||||
jose::jwk::PublicJsonWebKeySet,
|
||||
requests::{
|
||||
authorization_code::{self, AuthorizationRequestData, AuthorizationValidationData},
|
||||
discovery,
|
||||
jose::{self, JwtVerificationData},
|
||||
userinfo,
|
||||
},
|
||||
types::{
|
||||
iana::jose::JsonWebSignatureAlg, oidc::VerifiedProviderMetadata,
|
||||
requests::AccessTokenResponse, IdToken,
|
||||
},
|
||||
requests::{authorization_code::AuthorizationValidationData, discovery},
|
||||
types::oidc::VerifiedProviderMetadata,
|
||||
};
|
||||
use rand::SeedableRng;
|
||||
use ruma::{api::client::error::ErrorKind, MilliSecondsSinceUnixEpoch, OwnedUserId, UserId};
|
||||
use ruma::{api::client::session::get_login_types::v3::IdentityProvider, OwnedUserId, UserId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use tokio::sync::{oneshot, OnceCell};
|
||||
use tokio::sync::OnceCell;
|
||||
use tracing::error;
|
||||
use url::Url;
|
||||
|
||||
use crate::services;
|
||||
|
||||
mod data;
|
||||
pub use data::Data;
|
||||
|
||||
pub const SSO_AUTH_EXPIRATION_SECS: u64 = 60 * 60;
|
||||
pub const SSO_TOKEN_EXPIRATION_SECS: u64 = 60 * 2;
|
||||
pub const SSO_SESSION_COOKIE: &str = "sso-auth";
|
||||
pub const SUBJECT_CLAIM_KEY: &str = "sub";
|
||||
|
||||
pub struct Service {
|
||||
db: &'static dyn Data,
|
||||
|
@ -69,7 +55,7 @@ impl Service {
|
|||
let providers = services().globals.config.idps.iter();
|
||||
|
||||
self.providers
|
||||
.get_or_try_init(|| {
|
||||
.get_or_try_init(|| async move {
|
||||
future::try_join_all(providers.map(Provider::fetch_metadata))
|
||||
.await
|
||||
.map(Vec::into_iter)
|
||||
|
@ -86,6 +72,12 @@ impl Service {
|
|||
providers.get(provider)
|
||||
}
|
||||
|
||||
pub fn login_type(&self) -> impl Iterator<Item = IdentityProvider> + '_ {
|
||||
let providers = self.providers.get().expect("");
|
||||
|
||||
providers.iter().map(|p| p.config.inner.clone())
|
||||
}
|
||||
|
||||
pub fn user_from_subject(&self, provider: &str, subject: &str) -> Result<Option<OwnedUserId>> {
|
||||
self.db.user_from_subject(provider, subject)
|
||||
}
|
||||
|
@ -111,30 +103,6 @@ impl Provider {
|
|||
Error::bad_config("Failed to fetch identity provider metadata.")
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_signing_keys(&self) -> Result<PublicJsonWebKeySet> {
|
||||
jose::fetch_jwks(&services().sso.service, self.metadata.jwks_uri())
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!("Failed to fetch signing keys for token endpoint: {}", e);
|
||||
|
||||
Error::bad_config("Failed to fetch signing keys for token endpoint.")
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn fetch_access_token(
|
||||
&self,
|
||||
auth_code: String,
|
||||
validation_data: AuthorizationValidationData,
|
||||
) -> Result<(AccessTokenResponse, Option<IdToken<'_>>)> {
|
||||
}
|
||||
|
||||
pub async fn fetch_userinfo(
|
||||
&self,
|
||||
access_token: &str,
|
||||
id_token: &IdToken<'_>,
|
||||
) -> Result<Option<HashMap<String, Value>>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for Provider {
|
||||
|
@ -157,105 +125,28 @@ impl Hash for Provider {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct RegistrationToken {
|
||||
pub info: RegistrationInfo,
|
||||
pub provider_id: String,
|
||||
pub unique_claim: String,
|
||||
pub redirect_uri: Url,
|
||||
pub expires_at: MilliSecondsSinceUnixEpoch,
|
||||
}
|
||||
|
||||
impl RegistrationToken {
|
||||
pub fn new(
|
||||
provider_id: String,
|
||||
unique_claim: String,
|
||||
redirect_uri: Url,
|
||||
info: RegistrationInfo,
|
||||
) -> Self {
|
||||
let expires_at = MilliSecondsSinceUnixEpoch::from_system_time(
|
||||
UNIX_EPOCH
|
||||
.checked_add(Duration::from_secs(REGISTRATION_EXPIRATION_SECS))
|
||||
.expect("SystemTime should not overflow"),
|
||||
)
|
||||
.expect("MilliSecondsSinceUnixEpoch is not too large");
|
||||
|
||||
Self {
|
||||
info,
|
||||
provider_id,
|
||||
unique_claim,
|
||||
redirect_uri,
|
||||
expires_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct RegistrationInfo {
|
||||
pub username: String,
|
||||
pub displayname: Option<String>,
|
||||
pub avatar_url: Option<Url>,
|
||||
pub email: Option<EmailAddress>,
|
||||
}
|
||||
|
||||
impl RegistrationInfo {
|
||||
pub fn new(
|
||||
claims: &HashMap<String, Value>,
|
||||
username: &str,
|
||||
displayname: &str,
|
||||
avatar_url: &str,
|
||||
email: &str,
|
||||
) -> Self {
|
||||
Self {
|
||||
username: claims
|
||||
.get(username)
|
||||
.and_then(|v| v.as_str())
|
||||
.map(ToOwned::to_owned)
|
||||
.unwrap_or_default(),
|
||||
displayname: claims
|
||||
.get(displayname)
|
||||
.and_then(|v| v.as_str())
|
||||
.map(ToOwned::to_owned),
|
||||
avatar_url: claims
|
||||
.get(avatar_url)
|
||||
.and_then(|v| v.as_str())
|
||||
.map(Url::parse)
|
||||
.and_then(Result::ok),
|
||||
email: claims
|
||||
.get(email)
|
||||
.and_then(|v| v.as_str())
|
||||
.map(EmailAddress::from_str)
|
||||
.and_then(Result::ok),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct LoginToken {
|
||||
pub inner: String,
|
||||
pub provider_id: String,
|
||||
pub user_id: OwnedUserId,
|
||||
|
||||
#[serde(rename = "exp")]
|
||||
expires_at: u64,
|
||||
pub iss: String,
|
||||
pub aud: OwnedUserId,
|
||||
pub sub: String,
|
||||
pub exp: u64,
|
||||
}
|
||||
|
||||
impl LoginToken {
|
||||
pub fn new(provider_id: String, user_id: OwnedUserId) -> Self {
|
||||
let expires_at = SystemTime::now()
|
||||
.checked_add(Duration::from_secs(LOGIN_TOKEN_EXPIRATION_SECS))
|
||||
.expect("SystemTime should not overflow")
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("SystemTime went backwards")
|
||||
.as_secs();
|
||||
|
||||
pub fn new(provider: String, user_id: OwnedUserId) -> Self {
|
||||
Self {
|
||||
inner: utils::random_string(TOKEN_LENGTH),
|
||||
provider_id,
|
||||
user_id,
|
||||
expires_at,
|
||||
iss: provider,
|
||||
aud: user_id,
|
||||
sub: utils::random_string(TOKEN_LENGTH),
|
||||
exp: utils::millis_since_unix_epoch()
|
||||
.checked_add(LOGIN_TOKEN_EXPIRATION_SECS * 1000)
|
||||
.expect("time overflow"),
|
||||
}
|
||||
}
|
||||
pub fn audience(&self) -> &UserId {
|
||||
&self.aud
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue