diff --git a/crates/cli/src/sync.rs b/crates/cli/src/sync.rs index 0a0e01ddc..5caee00c5 100644 --- a/crates/cli/src/sync.rs +++ b/crates/cli/src/sync.rs @@ -231,6 +231,15 @@ pub async fn config_sync( } }; + let user_profile_method = match provider.user_profile_method { + mas_config::UpstreamOAuth2UserProfileMethod::Auto => { + mas_data_model::UpstreamOAuthProviderUserProfileMethod::Auto + } + mas_config::UpstreamOAuth2UserProfileMethod::UserinfoEndpoint => { + mas_data_model::UpstreamOAuthProviderUserProfileMethod::UserinfoEndpoint + } + }; + repo.upstream_oauth_provider() .upsert( clock, @@ -241,6 +250,7 @@ pub async fn config_sync( brand_name: provider.brand_name, scope: provider.scope.parse()?, token_endpoint_auth_method: provider.token_endpoint_auth_method.into(), + user_profile_method, token_endpoint_signing_alg: provider .token_endpoint_auth_signing_alg .clone(), @@ -248,6 +258,7 @@ pub async fn config_sync( encrypted_client_secret, claims_imports: map_claims_imports(&provider.claims_imports), token_endpoint_override: provider.token_endpoint, + userinfo_endpoint_override: provider.userinfo_endpoint, authorization_endpoint_override: provider.authorization_endpoint, jwks_uri_override: provider.jwks_uri, discovery_mode, diff --git a/crates/config/src/sections/mod.rs b/crates/config/src/sections/mod.rs index b21957aac..94bedba00 100644 --- a/crates/config/src/sections/mod.rs +++ b/crates/config/src/sections/mod.rs @@ -52,6 +52,7 @@ pub use self::{ EmailImportPreference as UpstreamOAuth2EmailImportPreference, ImportAction as UpstreamOAuth2ImportAction, PkceMethod as UpstreamOAuth2PkceMethod, SetEmailVerification as UpstreamOAuth2SetEmailVerification, UpstreamOAuth2Config, + UserProfileMethod as UpstreamOAuth2UserProfileMethod, }, }; use crate::util::ConfigurationSection; diff --git a/crates/config/src/sections/upstream_oauth2.rs b/crates/config/src/sections/upstream_oauth2.rs index 4742b2775..03488354f 100644 --- a/crates/config/src/sections/upstream_oauth2.rs +++ b/crates/config/src/sections/upstream_oauth2.rs @@ -124,6 +124,26 @@ impl From for OAuthClientAuthenticationMethod { } } +/// Whether to fetch the user profile from the userinfo endpoint, +/// or to rely on the data returned in the id_token from the token_endpoint +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum UserProfileMethod { + /// Use the userinfo endpoint if `openid` is not included in `scopes` + #[default] + Auto, + + /// Always use the userinfo endpoint + UserinfoEndpoint, +} + +impl UserProfileMethod { + #[allow(clippy::trivially_copy_pass_by_ref)] + const fn is_default(&self) -> bool { + matches!(self, UserProfileMethod::Auto) + } +} + /// How to handle a claim #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)] #[serde(rename_all = "lowercase")] @@ -401,6 +421,14 @@ pub struct Provider { #[serde(skip_serializing_if = "Option::is_none")] pub token_endpoint_auth_signing_alg: Option, + /// Whether to fetch the user profile from the userinfo endpoint, + /// or to rely on the data returned in the id_token from the token_endpoint. + /// + /// Defaults to `auto`, which uses the userinfo endpoint if `openid` is not + /// included in `scopes`, and the ID token otherwise. + #[serde(default, skip_serializing_if = "UserProfileMethod::is_default")] + pub user_profile_method: UserProfileMethod, + /// The scopes to request from the provider pub scope: String, @@ -424,6 +452,12 @@ pub struct Provider { #[serde(skip_serializing_if = "Option::is_none")] pub authorization_endpoint: Option, + /// The URL to use for the provider's userinfo endpoint + /// + /// Defaults to the `userinfo_endpoint` provided through discovery + #[serde(skip_serializing_if = "Option::is_none")] + pub userinfo_endpoint: Option, + /// The URL to use for the provider's token endpoint /// /// Defaults to the `token_endpoint` provided through discovery diff --git a/crates/data-model/src/lib.rs b/crates/data-model/src/lib.rs index 939e904f3..ae58b8e7c 100644 --- a/crates/data-model/src/lib.rs +++ b/crates/data-model/src/lib.rs @@ -42,6 +42,7 @@ pub use self::{ UpstreamOAuthProviderClaimsImports, UpstreamOAuthProviderDiscoveryMode, UpstreamOAuthProviderImportAction, UpstreamOAuthProviderImportPreference, UpstreamOAuthProviderPkceMode, UpstreamOAuthProviderSubjectPreference, + UpstreamOAuthProviderUserProfileMethod, }, user_agent::{DeviceType, UserAgent}, users::{ diff --git a/crates/data-model/src/upstream_oauth2/mod.rs b/crates/data-model/src/upstream_oauth2/mod.rs index cfa21ea1a..b998eac85 100644 --- a/crates/data-model/src/upstream_oauth2/mod.rs +++ b/crates/data-model/src/upstream_oauth2/mod.rs @@ -18,6 +18,7 @@ pub use self::{ PkceMode as UpstreamOAuthProviderPkceMode, SetEmailVerification as UpsreamOAuthProviderSetEmailVerification, SubjectPreference as UpstreamOAuthProviderSubjectPreference, UpstreamOAuthProvider, + UserProfileMethod as UpstreamOAuthProviderUserProfileMethod, }, session::{UpstreamOAuthAuthorizationSession, UpstreamOAuthAuthorizationSessionState}, }; diff --git a/crates/data-model/src/upstream_oauth2/provider.rs b/crates/data-model/src/upstream_oauth2/provider.rs index 0cb976a73..1688510d3 100644 --- a/crates/data-model/src/upstream_oauth2/provider.rs +++ b/crates/data-model/src/upstream_oauth2/provider.rs @@ -116,6 +116,51 @@ impl std::fmt::Display for PkceMode { } } +/// Whether to fetch the user profile from the userinfo endpoint, +/// or to rely on the data returned in the id_token from the token_endpoint +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] +#[serde(rename_all = "lowercase")] +pub enum UserProfileMethod { + /// Use the userinfo endpoint if `openid` is not included in `scopes` + #[default] + Auto, + + /// Always use the userinfo endpoint + UserinfoEndpoint, +} + +#[derive(Debug, Clone, Error)] +#[error("Invalid user profile method {0:?}")] +pub struct InvalidUserProfileMethodError(String); + +impl std::str::FromStr for UserProfileMethod { + type Err = InvalidUserProfileMethodError; + + fn from_str(s: &str) -> Result { + match s { + "auto" => Ok(Self::Auto), + "userinfo_endpoint" => Ok(Self::UserinfoEndpoint), + s => Err(InvalidUserProfileMethodError(s.to_owned())), + } + } +} + +impl UserProfileMethod { + #[must_use] + pub fn as_str(self) -> &'static str { + match self { + Self::Auto => "auto", + Self::UserinfoEndpoint => "userinfo_endpoint", + } + } +} + +impl std::fmt::Display for UserProfileMethod { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.as_str()) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct UpstreamOAuthProvider { pub id: Ulid, @@ -127,11 +172,13 @@ pub struct UpstreamOAuthProvider { pub jwks_uri_override: Option, pub authorization_endpoint_override: Option, pub token_endpoint_override: Option, + pub userinfo_endpoint_override: Option, pub scope: Scope, pub client_id: String, pub encrypted_client_secret: Option, pub token_endpoint_signing_alg: Option, pub token_endpoint_auth_method: OAuthClientAuthenticationMethod, + pub user_profile_method: UserProfileMethod, pub created_at: DateTime, pub disabled_at: Option>, pub claims_imports: ClaimsImports, diff --git a/crates/handlers/src/upstream_oauth2/cache.rs b/crates/handlers/src/upstream_oauth2/cache.rs index d1530fdaf..07d79d23f 100644 --- a/crates/handlers/src/upstream_oauth2/cache.rs +++ b/crates/handlers/src/upstream_oauth2/cache.rs @@ -109,6 +109,14 @@ impl<'a> LazyProviderInfos<'a> { Ok(self.load().await?.token_endpoint()) } + pub async fn userinfo_endpoint(&mut self) -> Result<&Url, DiscoveryError> { + if let Some(userinfo_endpoint) = &self.provider.userinfo_endpoint_override { + return Ok(userinfo_endpoint); + } + + Ok(self.load().await?.userinfo_endpoint()) + } + /// Get the PKCE methods supported by the provider. /// /// If the mode is set to auto, it will use the ones from discovery, @@ -276,7 +284,9 @@ mod tests { use std::sync::atomic::{AtomicUsize, Ordering}; use hyper::{body::Bytes, Request, Response, StatusCode}; - use mas_data_model::UpstreamOAuthProviderClaimsImports; + use mas_data_model::{ + UpstreamOAuthProviderClaimsImports, UpstreamOAuthProviderUserProfileMethod, + }; use mas_http::BoxCloneSyncService; use mas_iana::oauth::OAuthClientAuthenticationMethod; use mas_storage::{clock::MockClock, Clock}; @@ -487,8 +497,10 @@ mod tests { brand_name: None, discovery_mode: UpstreamOAuthProviderDiscoveryMode::Oidc, pkce_mode: UpstreamOAuthProviderPkceMode::Auto, + user_profile_method: UpstreamOAuthProviderUserProfileMethod::Auto, jwks_uri_override: None, authorization_endpoint_override: None, + userinfo_endpoint_override: None, token_endpoint_override: None, scope: Scope::from_iter([OPENID]), client_id: "client_id".to_owned(), diff --git a/crates/handlers/src/upstream_oauth2/callback.rs b/crates/handlers/src/upstream_oauth2/callback.rs index ce4b89607..b74e41927 100644 --- a/crates/handlers/src/upstream_oauth2/callback.rs +++ b/crates/handlers/src/upstream_oauth2/callback.rs @@ -13,6 +13,7 @@ use mas_axum_utils::{ cookies::CookieJar, http_client_factory::HttpClientFactory, sentry::SentryEventID, }; use mas_data_model::UpstreamOAuthProvider; +use mas_data_model::UpstreamOAuthProviderUserProfileMethod; use mas_keystore::{Encrypter, Keystore}; use mas_oidc_client::requests::{ authorization_code::AuthorizationValidationData, jose::JwtVerificationData, @@ -94,13 +95,14 @@ pub(crate) enum RouteError { MissingCookie, #[error(transparent)] - Internal(Box), + Internal(Box), } impl_from_error_for_route!(mas_storage::RepositoryError); impl_from_error_for_route!(mas_oidc_client::error::DiscoveryError); impl_from_error_for_route!(mas_oidc_client::error::JwksError); impl_from_error_for_route!(mas_oidc_client::error::TokenAuthorizationCodeError); +impl_from_error_for_route!(mas_oidc_client::error::UserInfoError); impl_from_error_for_route!(super::ProviderCredentialsError); impl_from_error_for_route!(super::cookie::UpstreamSessionNotFound); @@ -212,7 +214,7 @@ pub(crate) async fn get( redirect_uri, }; - let id_token_verification_data = JwtVerificationData { + let verification_data = JwtVerificationData { issuer: &provider.issuer, jwks: &jwks, // TODO: make that configurable @@ -220,24 +222,46 @@ pub(crate) async fn get( client_id: &provider.client_id, }; - let (response, id_token) = + let (response, id_token_map) = mas_oidc_client::requests::authorization_code::access_token_with_authorization_code( &http_service, client_credentials, lazy_metadata.token_endpoint().await?, code, validation_data, - Some(id_token_verification_data), + Some(verification_data), clock.now(), &mut rng, ) .await?; - let (_header, id_token) = id_token.ok_or(RouteError::MissingIDToken)?.into_parts(); + let (_header, id_token) = id_token_map + .clone() + .ok_or(RouteError::MissingIDToken)? + .into_parts(); + + let use_userinfo_endpoint = match provider.user_profile_method { + UpstreamOAuthProviderUserProfileMethod::Auto => !provider.scope.contains("openid"), + UpstreamOAuthProviderUserProfileMethod::UserinfoEndpoint => true, + }; + + let userinfo = if use_userinfo_endpoint { + let user_info_resp = mas_oidc_client::requests::userinfo::fetch_userinfo( + &http_service, + lazy_metadata.userinfo_endpoint().await?, + response.access_token.as_str(), + Some(verification_data), + &id_token_map.ok_or(RouteError::MissingIDToken)?, + ) + .await?; + minijinja::Value::from_serialize(&user_info_resp) + } else { + minijinja::Value::from_serialize(&id_token) + }; let env = { let mut env = environment(); - env.add_global("user", minijinja::Value::from_serialize(&id_token)); + env.add_global("user", userinfo); env }; diff --git a/crates/handlers/src/upstream_oauth2/link.rs b/crates/handlers/src/upstream_oauth2/link.rs index 3f7407e72..43bc0adc8 100644 --- a/crates/handlers/src/upstream_oauth2/link.rs +++ b/crates/handlers/src/upstream_oauth2/link.rs @@ -907,12 +907,15 @@ mod tests { brand_name: None, scope: Scope::from_iter([OPENID]), token_endpoint_auth_method: OAuthClientAuthenticationMethod::None, + user_profile_method: + mas_data_model::UpstreamOAuthProviderUserProfileMethod::Auto, token_endpoint_signing_alg: None, client_id: "client".to_owned(), encrypted_client_secret: None, claims_imports, authorization_endpoint_override: None, token_endpoint_override: None, + userinfo_endpoint_override: None, jwks_uri_override: None, discovery_mode: mas_data_model::UpstreamOAuthProviderDiscoveryMode::Oidc, pkce_mode: mas_data_model::UpstreamOAuthProviderPkceMode::Auto, diff --git a/crates/handlers/src/views/login.rs b/crates/handlers/src/views/login.rs index 3a65c3284..dae5d1bf6 100644 --- a/crates/handlers/src/views/login.rs +++ b/crates/handlers/src/views/login.rs @@ -371,11 +371,14 @@ mod test { scope: [OPENID].into_iter().collect(), token_endpoint_auth_method: OAuthClientAuthenticationMethod::None, token_endpoint_signing_alg: None, + user_profile_method: + mas_data_model::UpstreamOAuthProviderUserProfileMethod::Auto, client_id: "client".to_owned(), encrypted_client_secret: None, claims_imports: UpstreamOAuthProviderClaimsImports::default(), authorization_endpoint_override: None, token_endpoint_override: None, + userinfo_endpoint_override: None, jwks_uri_override: None, discovery_mode: mas_data_model::UpstreamOAuthProviderDiscoveryMode::Oidc, pkce_mode: mas_data_model::UpstreamOAuthProviderPkceMode::Auto, @@ -406,11 +409,14 @@ mod test { scope: [OPENID].into_iter().collect(), token_endpoint_auth_method: OAuthClientAuthenticationMethod::None, token_endpoint_signing_alg: None, + user_profile_method: + mas_data_model::UpstreamOAuthProviderUserProfileMethod::Auto, client_id: "client".to_owned(), encrypted_client_secret: None, claims_imports: UpstreamOAuthProviderClaimsImports::default(), authorization_endpoint_override: None, token_endpoint_override: None, + userinfo_endpoint_override: None, jwks_uri_override: None, discovery_mode: mas_data_model::UpstreamOAuthProviderDiscoveryMode::Oidc, pkce_mode: mas_data_model::UpstreamOAuthProviderPkceMode::Auto, diff --git a/crates/oauth2-types/src/oidc.rs b/crates/oauth2-types/src/oidc.rs index 011caaa9c..ab26d225c 100644 --- a/crates/oauth2-types/src/oidc.rs +++ b/crates/oauth2-types/src/oidc.rs @@ -976,6 +976,15 @@ impl VerifiedProviderMetadata { } } + /// TODO + #[must_use] + pub fn userinfo_endpoint(&self) -> &Url { + match &self.userinfo_endpoint { + Some(u) => u, + None => unreachable!(), + } + } + /// URL of the authorization server's token endpoint. #[must_use] pub fn token_endpoint(&self) -> &Url { diff --git a/crates/storage-pg/.sqlx/query-1f131aa966a4358d83e7247d3e30451f8bcf5df20faf46a4a4c0d4a36d1ff173.json b/crates/storage-pg/.sqlx/query-1f131aa966a4358d83e7247d3e30451f8bcf5df20faf46a4a4c0d4a36d1ff173.json deleted file mode 100644 index 2a92e950c..000000000 --- a/crates/storage-pg/.sqlx/query-1f131aa966a4358d83e7247d3e30451f8bcf5df20faf46a4a4c0d4a36d1ff173.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO upstream_oauth_providers (\n upstream_oauth_provider_id,\n issuer,\n human_name,\n brand_name,\n scope,\n token_endpoint_auth_method,\n token_endpoint_signing_alg,\n client_id,\n encrypted_client_secret,\n claims_imports,\n authorization_endpoint_override,\n token_endpoint_override,\n jwks_uri_override,\n discovery_mode,\n pkce_mode,\n created_at\n ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9,\n $10, $11, $12, $13, $14, $15, $16)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Text", - "Text", - "Text", - "Text", - "Text", - "Text", - "Text", - "Text", - "Jsonb", - "Text", - "Text", - "Text", - "Text", - "Text", - "Timestamptz" - ] - }, - "nullable": [] - }, - "hash": "1f131aa966a4358d83e7247d3e30451f8bcf5df20faf46a4a4c0d4a36d1ff173" -} diff --git a/crates/storage-pg/.sqlx/query-5d9f3d47ce6164b3f81aa09ef4fd8d5cd070945fd497d209ac1df99abcfb7c5d.json b/crates/storage-pg/.sqlx/query-4e5d21df2707243a2170e1ab2d1c2daf980beda7af2fd30591ec3c12cfa59991.json similarity index 73% rename from crates/storage-pg/.sqlx/query-5d9f3d47ce6164b3f81aa09ef4fd8d5cd070945fd497d209ac1df99abcfb7c5d.json rename to crates/storage-pg/.sqlx/query-4e5d21df2707243a2170e1ab2d1c2daf980beda7af2fd30591ec3c12cfa59991.json index f6f2b0dbc..5a36fce17 100644 --- a/crates/storage-pg/.sqlx/query-5d9f3d47ce6164b3f81aa09ef4fd8d5cd070945fd497d209ac1df99abcfb7c5d.json +++ b/crates/storage-pg/.sqlx/query-4e5d21df2707243a2170e1ab2d1c2daf980beda7af2fd30591ec3c12cfa59991.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n upstream_oauth_provider_id,\n issuer,\n human_name,\n brand_name,\n scope,\n client_id,\n encrypted_client_secret,\n token_endpoint_signing_alg,\n token_endpoint_auth_method,\n created_at,\n disabled_at,\n claims_imports as \"claims_imports: Json\",\n jwks_uri_override,\n authorization_endpoint_override,\n token_endpoint_override,\n discovery_mode,\n pkce_mode,\n additional_parameters as \"additional_parameters: Json>\"\n FROM upstream_oauth_providers\n WHERE disabled_at IS NULL\n ", + "query": "\n SELECT\n upstream_oauth_provider_id,\n issuer,\n human_name,\n brand_name,\n scope,\n client_id,\n encrypted_client_secret,\n token_endpoint_signing_alg,\n token_endpoint_auth_method,\n user_profile_method,\n created_at,\n disabled_at,\n claims_imports as \"claims_imports: Json\",\n jwks_uri_override,\n authorization_endpoint_override,\n token_endpoint_override,\n userinfo_endpoint_override,\n discovery_mode,\n pkce_mode,\n additional_parameters as \"additional_parameters: Json>\"\n FROM upstream_oauth_providers\n WHERE disabled_at IS NULL\n ", "describe": { "columns": [ { @@ -50,46 +50,56 @@ }, { "ordinal": 9, + "name": "user_profile_method", + "type_info": "Text" + }, + { + "ordinal": 10, "name": "created_at", "type_info": "Timestamptz" }, { - "ordinal": 10, + "ordinal": 11, "name": "disabled_at", "type_info": "Timestamptz" }, { - "ordinal": 11, + "ordinal": 12, "name": "claims_imports: Json", "type_info": "Jsonb" }, { - "ordinal": 12, + "ordinal": 13, "name": "jwks_uri_override", "type_info": "Text" }, { - "ordinal": 13, + "ordinal": 14, "name": "authorization_endpoint_override", "type_info": "Text" }, { - "ordinal": 14, + "ordinal": 15, "name": "token_endpoint_override", "type_info": "Text" }, { - "ordinal": 15, + "ordinal": 16, + "name": "userinfo_endpoint_override", + "type_info": "Text" + }, + { + "ordinal": 17, "name": "discovery_mode", "type_info": "Text" }, { - "ordinal": 16, + "ordinal": 18, "name": "pkce_mode", "type_info": "Text" }, { - "ordinal": 17, + "ordinal": 19, "name": "additional_parameters: Json>", "type_info": "Jsonb" } @@ -108,15 +118,17 @@ true, false, false, + false, true, false, true, true, true, + true, false, false, true ] }, - "hash": "5d9f3d47ce6164b3f81aa09ef4fd8d5cd070945fd497d209ac1df99abcfb7c5d" + "hash": "4e5d21df2707243a2170e1ab2d1c2daf980beda7af2fd30591ec3c12cfa59991" } diff --git a/crates/storage-pg/.sqlx/query-64af82b75223c88bee5cce91a9d7aa4dcea5d477f8fc9271fbdcc4b367b0074b.json b/crates/storage-pg/.sqlx/query-64af82b75223c88bee5cce91a9d7aa4dcea5d477f8fc9271fbdcc4b367b0074b.json new file mode 100644 index 000000000..d92b6a966 --- /dev/null +++ b/crates/storage-pg/.sqlx/query-64af82b75223c88bee5cce91a9d7aa4dcea5d477f8fc9271fbdcc4b367b0074b.json @@ -0,0 +1,40 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO upstream_oauth_providers (\n upstream_oauth_provider_id,\n issuer,\n human_name,\n brand_name,\n scope,\n token_endpoint_auth_method,\n user_profile_method,\n token_endpoint_signing_alg,\n client_id,\n encrypted_client_secret,\n claims_imports,\n authorization_endpoint_override,\n token_endpoint_override,\n userinfo_endpoint_override,\n jwks_uri_override,\n discovery_mode,\n pkce_mode,\n additional_parameters,\n created_at\n ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11,\n $12, $13, $14, $15, $16, $17, $18, $19)\n ON CONFLICT (upstream_oauth_provider_id)\n DO UPDATE\n SET\n issuer = EXCLUDED.issuer,\n human_name = EXCLUDED.human_name,\n brand_name = EXCLUDED.brand_name,\n scope = EXCLUDED.scope,\n token_endpoint_auth_method = EXCLUDED.token_endpoint_auth_method,\n user_profile_method = EXCLUDED.user_profile_method,\n token_endpoint_signing_alg = EXCLUDED.token_endpoint_signing_alg,\n disabled_at = NULL,\n client_id = EXCLUDED.client_id,\n encrypted_client_secret = EXCLUDED.encrypted_client_secret,\n claims_imports = EXCLUDED.claims_imports,\n authorization_endpoint_override = EXCLUDED.authorization_endpoint_override,\n token_endpoint_override = EXCLUDED.token_endpoint_override,\n userinfo_endpoint_override = EXCLUDED.userinfo_endpoint_override,\n jwks_uri_override = EXCLUDED.jwks_uri_override,\n discovery_mode = EXCLUDED.discovery_mode,\n pkce_mode = EXCLUDED.pkce_mode,\n additional_parameters = EXCLUDED.additional_parameters\n RETURNING created_at\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "created_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Jsonb", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Jsonb", + "Timestamptz" + ] + }, + "nullable": [ + false + ] + }, + "hash": "64af82b75223c88bee5cce91a9d7aa4dcea5d477f8fc9271fbdcc4b367b0074b" +} diff --git a/crates/storage-pg/.sqlx/query-6bc80450f55b84e9d8e3ce304f0cfc163aba5bce95a9d6bd17ecba98af1eef98.json b/crates/storage-pg/.sqlx/query-6bc80450f55b84e9d8e3ce304f0cfc163aba5bce95a9d6bd17ecba98af1eef98.json new file mode 100644 index 000000000..b712fc29b --- /dev/null +++ b/crates/storage-pg/.sqlx/query-6bc80450f55b84e9d8e3ce304f0cfc163aba5bce95a9d6bd17ecba98af1eef98.json @@ -0,0 +1,31 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO upstream_oauth_providers (\n upstream_oauth_provider_id,\n issuer,\n human_name,\n brand_name,\n scope,\n token_endpoint_auth_method,\n user_profile_method,\n token_endpoint_signing_alg,\n client_id,\n encrypted_client_secret,\n claims_imports,\n authorization_endpoint_override,\n token_endpoint_override,\n userinfo_endpoint_override,\n jwks_uri_override,\n discovery_mode,\n pkce_mode,\n created_at\n ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,\n $11, $12, $13, $14, $15, $16, $17, $18)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Jsonb", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Timestamptz" + ] + }, + "nullable": [] + }, + "hash": "6bc80450f55b84e9d8e3ce304f0cfc163aba5bce95a9d6bd17ecba98af1eef98" +} diff --git a/crates/storage-pg/.sqlx/query-9aa8fa3a6277f67b2bf5a5ea5429a61e7997ff4f3e8d0dc772448a1f97e1e390.json b/crates/storage-pg/.sqlx/query-9aa8fa3a6277f67b2bf5a5ea5429a61e7997ff4f3e8d0dc772448a1f97e1e390.json deleted file mode 100644 index c016eb215..000000000 --- a/crates/storage-pg/.sqlx/query-9aa8fa3a6277f67b2bf5a5ea5429a61e7997ff4f3e8d0dc772448a1f97e1e390.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO upstream_oauth_providers (\n upstream_oauth_provider_id,\n issuer,\n human_name,\n brand_name,\n scope,\n token_endpoint_auth_method,\n token_endpoint_signing_alg,\n client_id,\n encrypted_client_secret,\n claims_imports,\n authorization_endpoint_override,\n token_endpoint_override,\n jwks_uri_override,\n discovery_mode,\n pkce_mode,\n additional_parameters,\n created_at\n ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9,\n $10, $11, $12, $13, $14, $15, $16, $17)\n ON CONFLICT (upstream_oauth_provider_id)\n DO UPDATE\n SET\n issuer = EXCLUDED.issuer,\n human_name = EXCLUDED.human_name,\n brand_name = EXCLUDED.brand_name,\n scope = EXCLUDED.scope,\n token_endpoint_auth_method = EXCLUDED.token_endpoint_auth_method,\n token_endpoint_signing_alg = EXCLUDED.token_endpoint_signing_alg,\n disabled_at = NULL,\n client_id = EXCLUDED.client_id,\n encrypted_client_secret = EXCLUDED.encrypted_client_secret,\n claims_imports = EXCLUDED.claims_imports,\n authorization_endpoint_override = EXCLUDED.authorization_endpoint_override,\n token_endpoint_override = EXCLUDED.token_endpoint_override,\n jwks_uri_override = EXCLUDED.jwks_uri_override,\n discovery_mode = EXCLUDED.discovery_mode,\n pkce_mode = EXCLUDED.pkce_mode,\n additional_parameters = EXCLUDED.additional_parameters\n RETURNING created_at\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "created_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "Text", - "Text", - "Text", - "Text", - "Text", - "Text", - "Text", - "Jsonb", - "Text", - "Text", - "Text", - "Text", - "Text", - "Jsonb", - "Timestamptz" - ] - }, - "nullable": [ - false - ] - }, - "hash": "9aa8fa3a6277f67b2bf5a5ea5429a61e7997ff4f3e8d0dc772448a1f97e1e390" -} diff --git a/crates/storage-pg/.sqlx/query-51b204376c63671a47b73ee8b3f8e669f90933f7e81ba744dca88d6bb94bf96a.json b/crates/storage-pg/.sqlx/query-b10c0c9618bfd0d48ded713c1565db4a472891a6e07f92717927695f043b5d11.json similarity index 72% rename from crates/storage-pg/.sqlx/query-51b204376c63671a47b73ee8b3f8e669f90933f7e81ba744dca88d6bb94bf96a.json rename to crates/storage-pg/.sqlx/query-b10c0c9618bfd0d48ded713c1565db4a472891a6e07f92717927695f043b5d11.json index ac9f681cd..fd7f2afc8 100644 --- a/crates/storage-pg/.sqlx/query-51b204376c63671a47b73ee8b3f8e669f90933f7e81ba744dca88d6bb94bf96a.json +++ b/crates/storage-pg/.sqlx/query-b10c0c9618bfd0d48ded713c1565db4a472891a6e07f92717927695f043b5d11.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n upstream_oauth_provider_id,\n issuer,\n human_name,\n brand_name,\n scope,\n client_id,\n encrypted_client_secret,\n token_endpoint_signing_alg,\n token_endpoint_auth_method,\n created_at,\n disabled_at,\n claims_imports as \"claims_imports: Json\",\n jwks_uri_override,\n authorization_endpoint_override,\n token_endpoint_override,\n discovery_mode,\n pkce_mode,\n additional_parameters as \"additional_parameters: Json>\"\n FROM upstream_oauth_providers\n WHERE upstream_oauth_provider_id = $1\n ", + "query": "\n SELECT\n upstream_oauth_provider_id,\n issuer,\n human_name,\n brand_name,\n scope,\n client_id,\n encrypted_client_secret,\n token_endpoint_signing_alg,\n token_endpoint_auth_method,\n user_profile_method,\n created_at,\n disabled_at,\n claims_imports as \"claims_imports: Json\",\n jwks_uri_override,\n authorization_endpoint_override,\n token_endpoint_override,\n userinfo_endpoint_override,\n discovery_mode,\n pkce_mode,\n additional_parameters as \"additional_parameters: Json>\"\n FROM upstream_oauth_providers\n WHERE upstream_oauth_provider_id = $1\n ", "describe": { "columns": [ { @@ -50,46 +50,56 @@ }, { "ordinal": 9, + "name": "user_profile_method", + "type_info": "Text" + }, + { + "ordinal": 10, "name": "created_at", "type_info": "Timestamptz" }, { - "ordinal": 10, + "ordinal": 11, "name": "disabled_at", "type_info": "Timestamptz" }, { - "ordinal": 11, + "ordinal": 12, "name": "claims_imports: Json", "type_info": "Jsonb" }, { - "ordinal": 12, + "ordinal": 13, "name": "jwks_uri_override", "type_info": "Text" }, { - "ordinal": 13, + "ordinal": 14, "name": "authorization_endpoint_override", "type_info": "Text" }, { - "ordinal": 14, + "ordinal": 15, "name": "token_endpoint_override", "type_info": "Text" }, { - "ordinal": 15, + "ordinal": 16, + "name": "userinfo_endpoint_override", + "type_info": "Text" + }, + { + "ordinal": 17, "name": "discovery_mode", "type_info": "Text" }, { - "ordinal": 16, + "ordinal": 18, "name": "pkce_mode", "type_info": "Text" }, { - "ordinal": 17, + "ordinal": 19, "name": "additional_parameters: Json>", "type_info": "Jsonb" } @@ -110,15 +120,17 @@ true, false, false, + false, true, false, true, true, true, + true, false, false, true ] }, - "hash": "51b204376c63671a47b73ee8b3f8e669f90933f7e81ba744dca88d6bb94bf96a" + "hash": "b10c0c9618bfd0d48ded713c1565db4a472891a6e07f92717927695f043b5d11" } diff --git a/crates/storage-pg/migrations/20241014145741_upstream_oauth_userinfo.sql b/crates/storage-pg/migrations/20241014145741_upstream_oauth_userinfo.sql new file mode 100644 index 000000000..e20d9edf5 --- /dev/null +++ b/crates/storage-pg/migrations/20241014145741_upstream_oauth_userinfo.sql @@ -0,0 +1,4 @@ +-- Add migration script here +ALTER TABLE "upstream_oauth_providers" + ADD COLUMN "user_profile_method" TEXT NOT NULL DEFAULT 'auto', + ADD COLUMN "userinfo_endpoint_override" TEXT; diff --git a/crates/storage-pg/src/iden.rs b/crates/storage-pg/src/iden.rs index a87bef0db..cb0a52acd 100644 --- a/crates/storage-pg/src/iden.rs +++ b/crates/storage-pg/src/iden.rs @@ -98,6 +98,7 @@ pub enum UpstreamOAuthProviders { EncryptedClientSecret, TokenEndpointSigningAlg, TokenEndpointAuthMethod, + UserProfileMethod, CreatedAt, DisabledAt, ClaimsImports, @@ -107,6 +108,7 @@ pub enum UpstreamOAuthProviders { JwksUriOverride, TokenEndpointOverride, AuthorizationEndpointOverride, + UserinfoEndpointOverride, } #[derive(sea_query::Iden)] diff --git a/crates/storage-pg/src/upstream_oauth2/mod.rs b/crates/storage-pg/src/upstream_oauth2/mod.rs index c9ca8afdc..780d8fbdd 100644 --- a/crates/storage-pg/src/upstream_oauth2/mod.rs +++ b/crates/storage-pg/src/upstream_oauth2/mod.rs @@ -59,12 +59,15 @@ mod tests { scope: Scope::from_iter([OPENID]), token_endpoint_auth_method: mas_iana::oauth::OAuthClientAuthenticationMethod::None, + user_profile_method: + mas_data_model::UpstreamOAuthProviderUserProfileMethod::Auto, token_endpoint_signing_alg: None, client_id: "client-id".to_owned(), encrypted_client_secret: None, claims_imports: UpstreamOAuthProviderClaimsImports::default(), token_endpoint_override: None, authorization_endpoint_override: None, + userinfo_endpoint_override: None, jwks_uri_override: None, discovery_mode: mas_data_model::UpstreamOAuthProviderDiscoveryMode::Oidc, pkce_mode: mas_data_model::UpstreamOAuthProviderPkceMode::Auto, @@ -301,12 +304,15 @@ mod tests { scope: scope.clone(), token_endpoint_auth_method: mas_iana::oauth::OAuthClientAuthenticationMethod::None, + user_profile_method: + mas_data_model::UpstreamOAuthProviderUserProfileMethod::Auto, token_endpoint_signing_alg: None, client_id, encrypted_client_secret: None, claims_imports: UpstreamOAuthProviderClaimsImports::default(), token_endpoint_override: None, authorization_endpoint_override: None, + userinfo_endpoint_override: None, jwks_uri_override: None, discovery_mode: mas_data_model::UpstreamOAuthProviderDiscoveryMode::Oidc, pkce_mode: mas_data_model::UpstreamOAuthProviderPkceMode::Auto, diff --git a/crates/storage-pg/src/upstream_oauth2/provider.rs b/crates/storage-pg/src/upstream_oauth2/provider.rs index 0c54992c3..c29dbebe8 100644 --- a/crates/storage-pg/src/upstream_oauth2/provider.rs +++ b/crates/storage-pg/src/upstream_oauth2/provider.rs @@ -56,12 +56,14 @@ struct ProviderLookup { encrypted_client_secret: Option, token_endpoint_signing_alg: Option, token_endpoint_auth_method: String, + user_profile_method: String, created_at: DateTime, disabled_at: Option>, claims_imports: Json, jwks_uri_override: Option, authorization_endpoint_override: Option, token_endpoint_override: Option, + userinfo_endpoint_override: Option, discovery_mode: String, pkce_mode: String, additional_parameters: Option>>, @@ -116,6 +118,17 @@ impl TryFrom for UpstreamOAuthProvider { .source(e) })?; + let userinfo_endpoint_override = value + .userinfo_endpoint_override + .map(|x| x.parse()) + .transpose() + .map_err(|e| { + DatabaseInconsistencyError::on("upstream_oauth_providers") + .column("userinfo_endpoint_override") + .row(id) + .source(e) + })?; + let jwks_uri_override = value .jwks_uri_override .map(|x| x.parse()) @@ -141,6 +154,13 @@ impl TryFrom for UpstreamOAuthProvider { .source(e) })?; + let user_profile_method = value.user_profile_method.parse().map_err(|e| { + DatabaseInconsistencyError::on("upstream_oauth_providers") + .column("user_profile_method") + .row(id) + .source(e) + })?; + let additional_authorization_parameters = value .additional_parameters .map(|Json(x)| x) @@ -155,12 +175,14 @@ impl TryFrom for UpstreamOAuthProvider { client_id: value.client_id, encrypted_client_secret: value.encrypted_client_secret, token_endpoint_auth_method, + user_profile_method, token_endpoint_signing_alg, created_at: value.created_at, disabled_at: value.disabled_at, claims_imports: value.claims_imports.0, authorization_endpoint_override, token_endpoint_override, + userinfo_endpoint_override, jwks_uri_override, discovery_mode, pkce_mode, @@ -209,12 +231,14 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<' encrypted_client_secret, token_endpoint_signing_alg, token_endpoint_auth_method, + user_profile_method, created_at, disabled_at, claims_imports as "claims_imports: Json", jwks_uri_override, authorization_endpoint_override, token_endpoint_override, + userinfo_endpoint_override, discovery_mode, pkce_mode, additional_parameters as "additional_parameters: Json>" @@ -265,18 +289,20 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<' brand_name, scope, token_endpoint_auth_method, + user_profile_method, token_endpoint_signing_alg, client_id, encrypted_client_secret, claims_imports, authorization_endpoint_override, token_endpoint_override, + userinfo_endpoint_override, jwks_uri_override, discovery_mode, pkce_mode, created_at - ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, - $10, $11, $12, $13, $14, $15, $16) + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, + $11, $12, $13, $14, $15, $16, $17, $18) "#, Uuid::from(id), ¶ms.issuer, @@ -284,6 +310,7 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<' params.brand_name.as_deref(), params.scope.to_string(), params.token_endpoint_auth_method.to_string(), + params.user_profile_method.to_string(), params .token_endpoint_signing_alg .as_ref() @@ -299,6 +326,10 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<' .token_endpoint_override .as_ref() .map(ToString::to_string), + params + .userinfo_endpoint_override + .as_ref() + .map(ToString::to_string), params.jwks_uri_override.as_ref().map(ToString::to_string), params.discovery_mode.as_str(), params.pkce_mode.as_str(), @@ -318,11 +349,13 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<' encrypted_client_secret: params.encrypted_client_secret, token_endpoint_signing_alg: params.token_endpoint_signing_alg, token_endpoint_auth_method: params.token_endpoint_auth_method, + user_profile_method: params.user_profile_method, created_at, disabled_at: None, claims_imports: params.claims_imports, authorization_endpoint_override: params.authorization_endpoint_override, token_endpoint_override: params.token_endpoint_override, + userinfo_endpoint_override: params.userinfo_endpoint_override, jwks_uri_override: params.jwks_uri_override, discovery_mode: params.discovery_mode, pkce_mode: params.pkce_mode, @@ -424,19 +457,21 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<' brand_name, scope, token_endpoint_auth_method, + user_profile_method, token_endpoint_signing_alg, client_id, encrypted_client_secret, claims_imports, authorization_endpoint_override, token_endpoint_override, + userinfo_endpoint_override, jwks_uri_override, discovery_mode, pkce_mode, additional_parameters, created_at - ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, - $10, $11, $12, $13, $14, $15, $16, $17) + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, + $12, $13, $14, $15, $16, $17, $18, $19) ON CONFLICT (upstream_oauth_provider_id) DO UPDATE SET @@ -445,6 +480,7 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<' brand_name = EXCLUDED.brand_name, scope = EXCLUDED.scope, token_endpoint_auth_method = EXCLUDED.token_endpoint_auth_method, + user_profile_method = EXCLUDED.user_profile_method, token_endpoint_signing_alg = EXCLUDED.token_endpoint_signing_alg, disabled_at = NULL, client_id = EXCLUDED.client_id, @@ -452,6 +488,7 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<' claims_imports = EXCLUDED.claims_imports, authorization_endpoint_override = EXCLUDED.authorization_endpoint_override, token_endpoint_override = EXCLUDED.token_endpoint_override, + userinfo_endpoint_override = EXCLUDED.userinfo_endpoint_override, jwks_uri_override = EXCLUDED.jwks_uri_override, discovery_mode = EXCLUDED.discovery_mode, pkce_mode = EXCLUDED.pkce_mode, @@ -464,6 +501,7 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<' params.brand_name.as_deref(), params.scope.to_string(), params.token_endpoint_auth_method.to_string(), + params.user_profile_method.to_string(), params .token_endpoint_signing_alg .as_ref() @@ -479,6 +517,10 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<' .token_endpoint_override .as_ref() .map(ToString::to_string), + params + .userinfo_endpoint_override + .as_ref() + .map(ToString::to_string), params.jwks_uri_override.as_ref().map(ToString::to_string), params.discovery_mode.as_str(), params.pkce_mode.as_str(), @@ -499,11 +541,13 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<' encrypted_client_secret: params.encrypted_client_secret, token_endpoint_signing_alg: params.token_endpoint_signing_alg, token_endpoint_auth_method: params.token_endpoint_auth_method, + user_profile_method: params.user_profile_method, created_at, disabled_at: None, claims_imports: params.claims_imports, authorization_endpoint_override: params.authorization_endpoint_override, token_endpoint_override: params.token_endpoint_override, + userinfo_endpoint_override: params.userinfo_endpoint_override, jwks_uri_override: params.jwks_uri_override, discovery_mode: params.discovery_mode, pkce_mode: params.pkce_mode, @@ -662,6 +706,13 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<' )), ProviderLookupIden::AuthorizationEndpointOverride, ) + .expr_as( + Expr::col(( + UpstreamOAuthProviders::Table, + UpstreamOAuthProviders::UserinfoEndpointOverride, + )), + ProviderLookupIden::UserinfoEndpointOverride, + ) .expr_as( Expr::col(( UpstreamOAuthProviders::Table, @@ -676,6 +727,13 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<' )), ProviderLookupIden::PkceMode, ) + .expr_as( + Expr::col(( + UpstreamOAuthProviders::Table, + UpstreamOAuthProviders::UserProfileMethod, + )), + ProviderLookupIden::UserProfileMethod, + ) .expr_as( Expr::col(( UpstreamOAuthProviders::Table, @@ -762,12 +820,14 @@ impl<'c> UpstreamOAuthProviderRepository for PgUpstreamOAuthProviderRepository<' encrypted_client_secret, token_endpoint_signing_alg, token_endpoint_auth_method, + user_profile_method, created_at, disabled_at, claims_imports as "claims_imports: Json", jwks_uri_override, authorization_endpoint_override, token_endpoint_override, + userinfo_endpoint_override, discovery_mode, pkce_mode, additional_parameters as "additional_parameters: Json>" diff --git a/crates/storage/src/upstream_oauth2/provider.rs b/crates/storage/src/upstream_oauth2/provider.rs index 7d7d4dbb0..6db60476d 100644 --- a/crates/storage/src/upstream_oauth2/provider.rs +++ b/crates/storage/src/upstream_oauth2/provider.rs @@ -9,7 +9,7 @@ use std::marker::PhantomData; use async_trait::async_trait; use mas_data_model::{ UpstreamOAuthProvider, UpstreamOAuthProviderClaimsImports, UpstreamOAuthProviderDiscoveryMode, - UpstreamOAuthProviderPkceMode, + UpstreamOAuthProviderPkceMode, UpstreamOAuthProviderUserProfileMethod, }; use mas_iana::{jose::JsonWebSignatureAlg, oauth::OAuthClientAuthenticationMethod}; use oauth2_types::scope::Scope; @@ -41,6 +41,10 @@ pub struct UpstreamOAuthProviderParams { /// `private_key_jwt` authentication methods are used pub token_endpoint_signing_alg: Option, + /// Whether to fetch the user profile from the userinfo endpoint, + /// or to rely on the data returned in the id_token from the token_endpoint. + pub user_profile_method: UpstreamOAuthProviderUserProfileMethod, + /// The client ID to use when authenticating to the upstream pub client_id: String, @@ -58,6 +62,9 @@ pub struct UpstreamOAuthProviderParams { /// discovered pub token_endpoint_override: Option, + /// TODO + pub userinfo_endpoint_override: Option, + /// The URL to use when fetching JWKS. If `None`, the URL will be discovered pub jwks_uri_override: Option, diff --git a/frontend/src/gql/gql.ts b/frontend/src/gql/gql.ts index 2d605bfca..87238b09d 100644 --- a/frontend/src/gql/gql.ts +++ b/frontend/src/gql/gql.ts @@ -11,6 +11,7 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/ * 3. It does not support dead code elimination, so it will add unused operations. * * Therefore it is highly recommended to use the babel or swc plugin for production. + * Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size */ const documents = { "\n fragment PasswordChange_siteConfig on SiteConfig {\n id\n passwordChangeAllowed\n }\n": types.PasswordChange_SiteConfigFragmentDoc, diff --git a/frontend/src/gql/graphql.ts b/frontend/src/gql/graphql.ts index 44fa3332d..47896a5bd 100644 --- a/frontend/src/gql/graphql.ts +++ b/frontend/src/gql/graphql.ts @@ -38,19 +38,6 @@ export type AddEmailInput = { userId: Scalars['ID']['input']; }; -/** The payload of the `addEmail` mutation */ -export type AddEmailPayload = { - __typename?: 'AddEmailPayload'; - /** The email address that was added */ - email?: Maybe; - /** Status of the operation */ - status: AddEmailStatus; - /** The user to whom the email address was added */ - user?: Maybe; - /** The list of policy violations if the email address was denied */ - violations?: Maybe>; -}; - /** The status of the `addEmail` mutation */ export enum AddEmailStatus { /** The email address was added */ @@ -77,15 +64,6 @@ export type AddUserInput = { username: Scalars['String']['input']; }; -/** The payload for the `addUser` mutation. */ -export type AddUserPayload = { - __typename?: 'AddUserPayload'; - /** Status of the operation */ - status: AddUserStatus; - /** The user that was added. */ - user?: Maybe; -}; - /** The status of the `addUser` mutation. */ export enum AddUserStatus { /** The user was added. */ @@ -104,123 +82,6 @@ export type AllowUserCrossSigningResetInput = { userId: Scalars['ID']['input']; }; -/** The payload for the `allowUserCrossSigningReset` mutation. */ -export type AllowUserCrossSigningResetPayload = { - __typename?: 'AllowUserCrossSigningResetPayload'; - /** The user that was updated. */ - user?: Maybe; -}; - -export type Anonymous = Node & { - __typename?: 'Anonymous'; - id: Scalars['ID']['output']; -}; - -/** A session in an application, either a compatibility or an OAuth 2.0 one */ -export type AppSession = CompatSession | Oauth2Session; - -export type AppSessionConnection = { - __typename?: 'AppSessionConnection'; - /** A list of edges. */ - edges: Array; - /** A list of nodes. */ - nodes: Array; - /** Information to aid in pagination. */ - pageInfo: PageInfo; - /** Identifies the total count of items in the connection. */ - totalCount: Scalars['Int']['output']; -}; - -/** An edge in a connection. */ -export type AppSessionEdge = { - __typename?: 'AppSessionEdge'; - /** A cursor for use in pagination */ - cursor: Scalars['String']['output']; - /** The item at the end of the edge */ - node: AppSession; -}; - -/** - * An authentication records when a user enter their credential in a browser - * session. - */ -export type Authentication = CreationEvent & Node & { - __typename?: 'Authentication'; - /** When the object was created. */ - createdAt: Scalars['DateTime']['output']; - /** ID of the object. */ - id: Scalars['ID']['output']; -}; - -/** A browser session represents a logged in user in a browser. */ -export type BrowserSession = CreationEvent & Node & { - __typename?: 'BrowserSession'; - /** - * Get the list of both compat and OAuth 2.0 sessions started by this - * browser session, chronologically sorted - */ - appSessions: AppSessionConnection; - /** When the object was created. */ - createdAt: Scalars['DateTime']['output']; - /** When the session was finished. */ - finishedAt?: Maybe; - /** ID of the object. */ - id: Scalars['ID']['output']; - /** The last time the session was active. */ - lastActiveAt?: Maybe; - /** The last IP address used by the session. */ - lastActiveIp?: Maybe; - /** The most recent authentication of this session. */ - lastAuthentication?: Maybe; - /** The state of the session. */ - state: SessionState; - /** The user logged in this session. */ - user: User; - /** The user-agent with which the session was created. */ - userAgent?: Maybe; -}; - - -/** A browser session represents a logged in user in a browser. */ -export type BrowserSessionAppSessionsArgs = { - after?: InputMaybe; - before?: InputMaybe; - device?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - state?: InputMaybe; -}; - -export type BrowserSessionConnection = { - __typename?: 'BrowserSessionConnection'; - /** A list of edges. */ - edges: Array; - /** A list of nodes. */ - nodes: Array; - /** Information to aid in pagination. */ - pageInfo: PageInfo; - /** Identifies the total count of items in the connection. */ - totalCount: Scalars['Int']['output']; -}; - -/** An edge in a connection. */ -export type BrowserSessionEdge = { - __typename?: 'BrowserSessionEdge'; - /** A cursor for use in pagination */ - cursor: Scalars['String']['output']; - /** The item at the end of the edge */ - node: BrowserSession; -}; - -export type CaptchaConfig = { - __typename?: 'CaptchaConfig'; - id: Scalars['ID']['output']; - /** Which Captcha service is being used */ - service: CaptchaService; - /** The site key used by the instance */ - siteKey: Scalars['String']['output']; -}; - /** Which Captcha service is being used */ export enum CaptchaService { CloudflareTurnstile = 'CLOUDFLARE_TURNSTILE', @@ -228,57 +89,6 @@ export enum CaptchaService { RecaptchaV2 = 'RECAPTCHA_V2' } -/** - * A compat session represents a client session which used the legacy Matrix - * login API. - */ -export type CompatSession = CreationEvent & Node & { - __typename?: 'CompatSession'; - /** The browser session which started this session, if any. */ - browserSession?: Maybe; - /** When the object was created. */ - createdAt: Scalars['DateTime']['output']; - /** The Matrix Device ID of this session. */ - deviceId: Scalars['String']['output']; - /** When the session ended. */ - finishedAt?: Maybe; - /** ID of the object. */ - id: Scalars['ID']['output']; - /** The last time the session was active. */ - lastActiveAt?: Maybe; - /** The last IP address used by the session. */ - lastActiveIp?: Maybe; - /** The associated SSO login, if any. */ - ssoLogin?: Maybe; - /** The state of the session. */ - state: SessionState; - /** The user authorized for this session. */ - user: User; - /** The user-agent with which the session was created. */ - userAgent?: Maybe; -}; - -export type CompatSessionConnection = { - __typename?: 'CompatSessionConnection'; - /** A list of edges. */ - edges: Array; - /** A list of nodes. */ - nodes: Array; - /** Information to aid in pagination. */ - pageInfo: PageInfo; - /** Identifies the total count of items in the connection. */ - totalCount: Scalars['Int']['output']; -}; - -/** An edge in a connection. */ -export type CompatSessionEdge = { - __typename?: 'CompatSessionEdge'; - /** A cursor for use in pagination */ - cursor: Scalars['String']['output']; - /** The item at the end of the edge */ - node: CompatSession; -}; - /** The type of a compatibility session. */ export enum CompatSessionType { /** The session was created by a SSO login. */ @@ -287,50 +97,6 @@ export enum CompatSessionType { Unknown = 'UNKNOWN' } -/** - * A compat SSO login represents a login done through the legacy Matrix login - * API, via the `m.login.sso` login method. - */ -export type CompatSsoLogin = Node & { - __typename?: 'CompatSsoLogin'; - /** When the object was created. */ - createdAt: Scalars['DateTime']['output']; - /** When the client exchanged the login token sent during the redirection. */ - exchangedAt?: Maybe; - /** - * When the login was fulfilled, and the user was redirected back to the - * client. - */ - fulfilledAt?: Maybe; - /** ID of the object. */ - id: Scalars['ID']['output']; - /** The redirect URI used during the login. */ - redirectUri: Scalars['Url']['output']; - /** The compat session which was started by this login. */ - session?: Maybe; -}; - -export type CompatSsoLoginConnection = { - __typename?: 'CompatSsoLoginConnection'; - /** A list of edges. */ - edges: Array; - /** A list of nodes. */ - nodes: Array; - /** Information to aid in pagination. */ - pageInfo: PageInfo; - /** Identifies the total count of items in the connection. */ - totalCount: Scalars['Int']['output']; -}; - -/** An edge in a connection. */ -export type CompatSsoLoginEdge = { - __typename?: 'CompatSsoLoginEdge'; - /** A cursor for use in pagination */ - cursor: Scalars['String']['output']; - /** The item at the end of the edge */ - node: CompatSsoLogin; -}; - /** The input of the `createOauth2Session` mutation. */ export type CreateOAuth2SessionInput = { /** Whether the session should issue a never-expiring access token */ @@ -341,23 +107,6 @@ export type CreateOAuth2SessionInput = { userId: Scalars['ID']['input']; }; -/** The payload of the `createOauth2Session` mutation. */ -export type CreateOAuth2SessionPayload = { - __typename?: 'CreateOAuth2SessionPayload'; - /** Access token for this session */ - accessToken: Scalars['String']['output']; - /** The OAuth 2.0 session which was just created */ - oauth2Session: Oauth2Session; - /** Refresh token for this session, if it is not a permanent session */ - refreshToken?: Maybe; -}; - -/** An object with a creation date. */ -export type CreationEvent = { - /** When the object was created. */ - createdAt: Scalars['DateTime']['output']; -}; - /** A filter for dates, with a lower bound and an upper bound */ export type DateFilter = { /** The lower bound of the date range */ @@ -384,14 +133,6 @@ export type EndBrowserSessionInput = { browserSessionId: Scalars['ID']['input']; }; -export type EndBrowserSessionPayload = { - __typename?: 'EndBrowserSessionPayload'; - /** Returns the ended session. */ - browserSession?: Maybe; - /** The status of the mutation. */ - status: EndBrowserSessionStatus; -}; - /** The status of the `endBrowserSession` mutation. */ export enum EndBrowserSessionStatus { /** The session was ended. */ @@ -406,14 +147,6 @@ export type EndCompatSessionInput = { compatSessionId: Scalars['ID']['input']; }; -export type EndCompatSessionPayload = { - __typename?: 'EndCompatSessionPayload'; - /** Returns the ended session. */ - compatSession?: Maybe; - /** The status of the mutation. */ - status: EndCompatSessionStatus; -}; - /** The status of the `endCompatSession` mutation. */ export enum EndCompatSessionStatus { /** The session was ended. */ @@ -428,14 +161,6 @@ export type EndOAuth2SessionInput = { oauth2SessionId: Scalars['ID']['input']; }; -export type EndOAuth2SessionPayload = { - __typename?: 'EndOAuth2SessionPayload'; - /** Returns the ended session. */ - oauth2Session?: Maybe; - /** The status of the mutation. */ - status: EndOAuth2SessionStatus; -}; - /** The status of the `endOauth2Session` mutation. */ export enum EndOAuth2SessionStatus { /** The session was ended. */ @@ -452,15 +177,6 @@ export type LockUserInput = { userId: Scalars['ID']['input']; }; -/** The payload for the `lockUser` mutation. */ -export type LockUserPayload = { - __typename?: 'LockUserPayload'; - /** Status of the operation */ - status: LockUserStatus; - /** The user that was locked. */ - user?: Maybe; -}; - /** The status of the `lockUser` mutation. */ export enum LockUserStatus { /** The user was locked. */ @@ -469,176 +185,6 @@ export enum LockUserStatus { NotFound = 'NOT_FOUND' } -export type MatrixUser = { - __typename?: 'MatrixUser'; - /** The avatar URL of the user, if any. */ - avatarUrl?: Maybe; - /** Whether the user is deactivated on the homeserver. */ - deactivated: Scalars['Boolean']['output']; - /** The display name of the user, if any. */ - displayName?: Maybe; - /** The Matrix ID of the user. */ - mxid: Scalars['String']['output']; -}; - -/** The mutations root of the GraphQL interface. */ -export type Mutation = { - __typename?: 'Mutation'; - /** Add an email address to the specified user */ - addEmail: AddEmailPayload; - /** Add a user. This is only available to administrators. */ - addUser: AddUserPayload; - /** Temporarily allow user to reset their cross-signing keys. */ - allowUserCrossSigningReset: AllowUserCrossSigningResetPayload; - /** - * Create a new arbitrary OAuth 2.0 Session. - * - * Only available for administrators. - */ - createOauth2Session: CreateOAuth2SessionPayload; - endBrowserSession: EndBrowserSessionPayload; - endCompatSession: EndCompatSessionPayload; - endOauth2Session: EndOAuth2SessionPayload; - /** Lock a user. This is only available to administrators. */ - lockUser: LockUserPayload; - /** Remove an email address */ - removeEmail: RemoveEmailPayload; - /** Send a verification code for an email address */ - sendVerificationEmail: SendVerificationEmailPayload; - /** - * Set whether a user can request admin. This is only available to - * administrators. - */ - setCanRequestAdmin: SetCanRequestAdminPayload; - /** Set the display name of a user */ - setDisplayName: SetDisplayNamePayload; - /** - * Set the password for a user. - * - * This can be used by server administrators to set any user's password, - * or, provided the capability hasn't been disabled on this server, - * by a user to change their own password as long as they know their - * current password. - */ - setPassword: SetPasswordPayload; - /** Set the password for yourself, using a recovery ticket sent by e-mail. */ - setPasswordByRecovery: SetPasswordPayload; - /** Set an email address as primary */ - setPrimaryEmail: SetPrimaryEmailPayload; - /** Unlock a user. This is only available to administrators. */ - unlockUser: UnlockUserPayload; - /** Submit a verification code for an email address */ - verifyEmail: VerifyEmailPayload; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationAddEmailArgs = { - input: AddEmailInput; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationAddUserArgs = { - input: AddUserInput; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationAllowUserCrossSigningResetArgs = { - input: AllowUserCrossSigningResetInput; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationCreateOauth2SessionArgs = { - input: CreateOAuth2SessionInput; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationEndBrowserSessionArgs = { - input: EndBrowserSessionInput; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationEndCompatSessionArgs = { - input: EndCompatSessionInput; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationEndOauth2SessionArgs = { - input: EndOAuth2SessionInput; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationLockUserArgs = { - input: LockUserInput; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationRemoveEmailArgs = { - input: RemoveEmailInput; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationSendVerificationEmailArgs = { - input: SendVerificationEmailInput; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationSetCanRequestAdminArgs = { - input: SetCanRequestAdminInput; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationSetDisplayNameArgs = { - input: SetDisplayNameInput; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationSetPasswordArgs = { - input: SetPasswordInput; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationSetPasswordByRecoveryArgs = { - input: SetPasswordByRecoveryInput; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationSetPrimaryEmailArgs = { - input: SetPrimaryEmailInput; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationUnlockUserArgs = { - input: UnlockUserInput; -}; - - -/** The mutations root of the GraphQL interface. */ -export type MutationVerifyEmailArgs = { - input: VerifyEmailInput; -}; - -/** An object with an ID. */ -export type Node = { - /** ID of the object. */ - id: Scalars['ID']['output']; -}; - /** The application type advertised by the client. */ export enum Oauth2ApplicationType { /** Client is a native application. */ @@ -647,248 +193,12 @@ export enum Oauth2ApplicationType { Web = 'WEB' } -/** An OAuth 2.0 client */ -export type Oauth2Client = Node & { - __typename?: 'Oauth2Client'; - /** The application type advertised by the client. */ - applicationType?: Maybe; - /** OAuth 2.0 client ID */ - clientId: Scalars['String']['output']; - /** Client name advertised by the client. */ - clientName?: Maybe; - /** Client URI advertised by the client. */ - clientUri?: Maybe; - /** ID of the object. */ - id: Scalars['ID']['output']; - /** Logo URI advertised by the client. */ - logoUri?: Maybe; - /** Privacy policy URI advertised by the client. */ - policyUri?: Maybe; - /** List of redirect URIs used for authorization grants by the client. */ - redirectUris: Array; - /** Terms of services URI advertised by the client. */ - tosUri?: Maybe; -}; - -/** - * An OAuth 2.0 session represents a client session which used the OAuth APIs - * to login. - */ -export type Oauth2Session = CreationEvent & Node & { - __typename?: 'Oauth2Session'; - /** The browser session which started this OAuth 2.0 session. */ - browserSession?: Maybe; - /** OAuth 2.0 client used by this session. */ - client: Oauth2Client; - /** When the object was created. */ - createdAt: Scalars['DateTime']['output']; - /** When the session ended. */ - finishedAt?: Maybe; - /** ID of the object. */ - id: Scalars['ID']['output']; - /** The last time the session was active. */ - lastActiveAt?: Maybe; - /** The last IP address used by the session. */ - lastActiveIp?: Maybe; - /** Scope granted for this session. */ - scope: Scalars['String']['output']; - /** The state of the session. */ - state: SessionState; - /** User authorized for this session. */ - user?: Maybe; - /** The user-agent with which the session was created. */ - userAgent?: Maybe; -}; - -export type Oauth2SessionConnection = { - __typename?: 'Oauth2SessionConnection'; - /** A list of edges. */ - edges: Array; - /** A list of nodes. */ - nodes: Array; - /** Information to aid in pagination. */ - pageInfo: PageInfo; - /** Identifies the total count of items in the connection. */ - totalCount: Scalars['Int']['output']; -}; - -/** An edge in a connection. */ -export type Oauth2SessionEdge = { - __typename?: 'Oauth2SessionEdge'; - /** A cursor for use in pagination */ - cursor: Scalars['String']['output']; - /** The item at the end of the edge */ - node: Oauth2Session; -}; - -/** Information about pagination in a connection */ -export type PageInfo = { - __typename?: 'PageInfo'; - /** When paginating forwards, the cursor to continue. */ - endCursor?: Maybe; - /** When paginating forwards, are there more items? */ - hasNextPage: Scalars['Boolean']['output']; - /** When paginating backwards, are there more items? */ - hasPreviousPage: Scalars['Boolean']['output']; - /** When paginating backwards, the cursor to continue. */ - startCursor?: Maybe; -}; - -/** The query root of the GraphQL interface. */ -export type Query = { - __typename?: 'Query'; - /** Fetch a browser session by its ID. */ - browserSession?: Maybe; - /** Fetch a compatible session by its ID. */ - compatSession?: Maybe; - /** - * Get the current logged in browser session - * @deprecated Use `viewerSession` instead. - */ - currentBrowserSession?: Maybe; - /** - * Get the current logged in user - * @deprecated Use `viewer` instead. - */ - currentUser?: Maybe; - /** Fetches an object given its ID. */ - node?: Maybe; - /** Fetch an OAuth 2.0 client by its ID. */ - oauth2Client?: Maybe; - /** Fetch an OAuth 2.0 session by its ID. */ - oauth2Session?: Maybe; - /** Lookup a compat or OAuth 2.0 session */ - session?: Maybe; - /** Get the current site configuration */ - siteConfig: SiteConfig; - /** Fetch an upstream OAuth 2.0 link by its ID. */ - upstreamOauth2Link?: Maybe; - /** Fetch an upstream OAuth 2.0 provider by its ID. */ - upstreamOauth2Provider?: Maybe; - /** Get a list of upstream OAuth 2.0 providers. */ - upstreamOauth2Providers: UpstreamOAuth2ProviderConnection; - /** Fetch a user by its ID. */ - user?: Maybe; - /** Fetch a user by its username. */ - userByUsername?: Maybe; - /** Fetch a user email by its ID. */ - userEmail?: Maybe; - /** - * Get a list of users. - * - * This is only available to administrators. - */ - users: UserConnection; - /** Get the viewer */ - viewer: Viewer; - /** Get the viewer's session */ - viewerSession: ViewerSession; -}; - - -/** The query root of the GraphQL interface. */ -export type QueryBrowserSessionArgs = { - id: Scalars['ID']['input']; -}; - - -/** The query root of the GraphQL interface. */ -export type QueryCompatSessionArgs = { - id: Scalars['ID']['input']; -}; - - -/** The query root of the GraphQL interface. */ -export type QueryNodeArgs = { - id: Scalars['ID']['input']; -}; - - -/** The query root of the GraphQL interface. */ -export type QueryOauth2ClientArgs = { - id: Scalars['ID']['input']; -}; - - -/** The query root of the GraphQL interface. */ -export type QueryOauth2SessionArgs = { - id: Scalars['ID']['input']; -}; - - -/** The query root of the GraphQL interface. */ -export type QuerySessionArgs = { - deviceId: Scalars['String']['input']; - userId: Scalars['ID']['input']; -}; - - -/** The query root of the GraphQL interface. */ -export type QueryUpstreamOauth2LinkArgs = { - id: Scalars['ID']['input']; -}; - - -/** The query root of the GraphQL interface. */ -export type QueryUpstreamOauth2ProviderArgs = { - id: Scalars['ID']['input']; -}; - - -/** The query root of the GraphQL interface. */ -export type QueryUpstreamOauth2ProvidersArgs = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; -}; - - -/** The query root of the GraphQL interface. */ -export type QueryUserArgs = { - id: Scalars['ID']['input']; -}; - - -/** The query root of the GraphQL interface. */ -export type QueryUserByUsernameArgs = { - username: Scalars['String']['input']; -}; - - -/** The query root of the GraphQL interface. */ -export type QueryUserEmailArgs = { - id: Scalars['ID']['input']; -}; - - -/** The query root of the GraphQL interface. */ -export type QueryUsersArgs = { - after?: InputMaybe; - before?: InputMaybe; - canRequestAdmin?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - state?: InputMaybe; -}; - /** The input for the `removeEmail` mutation */ export type RemoveEmailInput = { /** The ID of the email address to remove */ userEmailId: Scalars['ID']['input']; }; -/** The payload of the `removeEmail` mutation */ -export type RemoveEmailPayload = { - __typename?: 'RemoveEmailPayload'; - /** The email address that was removed */ - email?: Maybe; - /** Status of the operation */ - status: RemoveEmailStatus; - /** The user to whom the email address belonged */ - user?: Maybe; -}; - /** The status of the `removeEmail` mutation */ export enum RemoveEmailStatus { /** The email address was not found */ @@ -905,17 +215,6 @@ export type SendVerificationEmailInput = { userEmailId: Scalars['ID']['input']; }; -/** The payload of the `sendVerificationEmail` mutation */ -export type SendVerificationEmailPayload = { - __typename?: 'SendVerificationEmailPayload'; - /** The email address to which the verification email was sent */ - email: UserEmail; - /** Status of the operation */ - status: SendVerificationEmailStatus; - /** The user to whom the email address belongs */ - user: User; -}; - /** The status of the `sendVerificationEmail` mutation */ export enum SendVerificationEmailStatus { /** The email address is already verified */ @@ -924,9 +223,6 @@ export enum SendVerificationEmailStatus { Sent = 'SENT' } -/** A client session, either compat or OAuth 2.0 */ -export type Session = CompatSession | Oauth2Session; - /** The state of a session */ export enum SessionState { /** The session is active. */ @@ -943,13 +239,6 @@ export type SetCanRequestAdminInput = { userId: Scalars['ID']['input']; }; -/** The payload for the `setCanRequestAdmin` mutation. */ -export type SetCanRequestAdminPayload = { - __typename?: 'SetCanRequestAdminPayload'; - /** The user that was updated. */ - user?: Maybe; -}; - /** The input for the `addEmail` mutation */ export type SetDisplayNameInput = { /** The display name to set. If `None`, the display name will be removed. */ @@ -958,15 +247,6 @@ export type SetDisplayNameInput = { userId: Scalars['ID']['input']; }; -/** The payload of the `setDisplayName` mutation */ -export type SetDisplayNamePayload = { - __typename?: 'SetDisplayNamePayload'; - /** Status of the operation */ - status: SetDisplayNameStatus; - /** The user that was updated */ - user?: Maybe; -}; - /** The status of the `setDisplayName` mutation */ export enum SetDisplayNameStatus { /** The display name is invalid */ @@ -1004,13 +284,6 @@ export type SetPasswordInput = { userId: Scalars['ID']['input']; }; -/** The return type for the `setPassword` mutation. */ -export type SetPasswordPayload = { - __typename?: 'SetPasswordPayload'; - /** Status of the operation */ - status: SetPasswordStatus; -}; - /** The status of the `setPassword` mutation. */ export enum SetPasswordStatus { /** Your account is locked and you can't change its password. */ @@ -1057,14 +330,6 @@ export type SetPrimaryEmailInput = { userEmailId: Scalars['ID']['input']; }; -/** The payload of the `setPrimaryEmail` mutation */ -export type SetPrimaryEmailPayload = { - __typename?: 'SetPrimaryEmailPayload'; - status: SetPrimaryEmailStatus; - /** The user to whom the email address belongs */ - user?: Maybe; -}; - /** The status of the `setPrimaryEmail` mutation */ export enum SetPrimaryEmailStatus { /** The email address was not found */ @@ -1075,53 +340,12 @@ export enum SetPrimaryEmailStatus { Unverified = 'UNVERIFIED' } -export type SiteConfig = Node & { - __typename?: 'SiteConfig'; - /** The configuration of CAPTCHA provider. */ - captchaConfig?: Maybe; - /** Whether users can change their display name. */ - displayNameChangeAllowed: Scalars['Boolean']['output']; - /** Whether users can change their email. */ - emailChangeAllowed: Scalars['Boolean']['output']; - /** The ID of the site configuration. */ - id: Scalars['ID']['output']; - /** Imprint to show in the footer. */ - imprint?: Maybe; - /** - * Minimum password complexity, from 0 to 4, in terms of a zxcvbn score. - * The exact scorer (including dictionaries and other data tables) - * in use is . - */ - minimumPasswordComplexity: Scalars['Int']['output']; - /** Whether passwords are enabled and users can change their own passwords. */ - passwordChangeAllowed: Scalars['Boolean']['output']; - /** Whether passwords are enabled for login. */ - passwordLoginEnabled: Scalars['Boolean']['output']; - /** Whether passwords are enabled and users can register using a password. */ - passwordRegistrationEnabled: Scalars['Boolean']['output']; - /** The URL to the privacy policy. */ - policyUri?: Maybe; - /** The server name of the homeserver. */ - serverName: Scalars['String']['output']; - /** The URL to the terms of service. */ - tosUri?: Maybe; -}; - /** The input for the `unlockUser` mutation. */ export type UnlockUserInput = { /** The ID of the user to unlock */ userId: Scalars['ID']['input']; }; -/** The payload for the `unlockUser` mutation. */ -export type UnlockUserPayload = { - __typename?: 'UnlockUserPayload'; - /** Status of the operation */ - status: UnlockUserStatus; - /** The user that was unlocked. */ - user?: Maybe; -}; - /** The status of the `unlockUser` mutation. */ export enum UnlockUserStatus { /** The user was not found. */ @@ -1130,263 +354,6 @@ export enum UnlockUserStatus { Unlocked = 'UNLOCKED' } -export type UpstreamOAuth2Link = CreationEvent & Node & { - __typename?: 'UpstreamOAuth2Link'; - /** When the object was created. */ - createdAt: Scalars['DateTime']['output']; - /** ID of the object. */ - id: Scalars['ID']['output']; - /** The provider for which this link is. */ - provider: UpstreamOAuth2Provider; - /** Subject used for linking */ - subject: Scalars['String']['output']; - /** The user to which this link is associated. */ - user?: Maybe; -}; - -export type UpstreamOAuth2LinkConnection = { - __typename?: 'UpstreamOAuth2LinkConnection'; - /** A list of edges. */ - edges: Array; - /** A list of nodes. */ - nodes: Array; - /** Information to aid in pagination. */ - pageInfo: PageInfo; - /** Identifies the total count of items in the connection. */ - totalCount: Scalars['Int']['output']; -}; - -/** An edge in a connection. */ -export type UpstreamOAuth2LinkEdge = { - __typename?: 'UpstreamOAuth2LinkEdge'; - /** A cursor for use in pagination */ - cursor: Scalars['String']['output']; - /** The item at the end of the edge */ - node: UpstreamOAuth2Link; -}; - -export type UpstreamOAuth2Provider = CreationEvent & Node & { - __typename?: 'UpstreamOAuth2Provider'; - /** Client ID used for this provider. */ - clientId: Scalars['String']['output']; - /** When the object was created. */ - createdAt: Scalars['DateTime']['output']; - /** ID of the object. */ - id: Scalars['ID']['output']; - /** OpenID Connect issuer URL. */ - issuer: Scalars['String']['output']; -}; - -export type UpstreamOAuth2ProviderConnection = { - __typename?: 'UpstreamOAuth2ProviderConnection'; - /** A list of edges. */ - edges: Array; - /** A list of nodes. */ - nodes: Array; - /** Information to aid in pagination. */ - pageInfo: PageInfo; - /** Identifies the total count of items in the connection. */ - totalCount: Scalars['Int']['output']; -}; - -/** An edge in a connection. */ -export type UpstreamOAuth2ProviderEdge = { - __typename?: 'UpstreamOAuth2ProviderEdge'; - /** A cursor for use in pagination */ - cursor: Scalars['String']['output']; - /** The item at the end of the edge */ - node: UpstreamOAuth2Provider; -}; - -/** A user is an individual's account. */ -export type User = Node & { - __typename?: 'User'; - /** - * Get the list of both compat and OAuth 2.0 sessions, chronologically - * sorted - */ - appSessions: AppSessionConnection; - /** Get the list of active browser sessions, chronologically sorted */ - browserSessions: BrowserSessionConnection; - /** Whether the user can request admin privileges. */ - canRequestAdmin: Scalars['Boolean']['output']; - /** Get the list of compatibility sessions, chronologically sorted */ - compatSessions: CompatSessionConnection; - /** Get the list of compatibility SSO logins, chronologically sorted */ - compatSsoLogins: CompatSsoLoginConnection; - /** When the object was created. */ - createdAt: Scalars['DateTime']['output']; - /** Get the list of emails, chronologically sorted */ - emails: UserEmailConnection; - /** ID of the object. */ - id: Scalars['ID']['output']; - /** When the user was locked out. */ - lockedAt?: Maybe; - /** Access to the user's Matrix account information. */ - matrix: MatrixUser; - /** Get the list of OAuth 2.0 sessions, chronologically sorted */ - oauth2Sessions: Oauth2SessionConnection; - /** Primary email address of the user. */ - primaryEmail?: Maybe; - /** Get the list of upstream OAuth 2.0 links */ - upstreamOauth2Links: UpstreamOAuth2LinkConnection; - /** Username chosen by the user. */ - username: Scalars['String']['output']; -}; - - -/** A user is an individual's account. */ -export type UserAppSessionsArgs = { - after?: InputMaybe; - before?: InputMaybe; - browserSession?: InputMaybe; - device?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - lastActive?: InputMaybe; - state?: InputMaybe; -}; - - -/** A user is an individual's account. */ -export type UserBrowserSessionsArgs = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - lastActive?: InputMaybe; - state?: InputMaybe; -}; - - -/** A user is an individual's account. */ -export type UserCompatSessionsArgs = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - lastActive?: InputMaybe; - state?: InputMaybe; - type?: InputMaybe; -}; - - -/** A user is an individual's account. */ -export type UserCompatSsoLoginsArgs = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; -}; - - -/** A user is an individual's account. */ -export type UserEmailsArgs = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - state?: InputMaybe; -}; - - -/** A user is an individual's account. */ -export type UserOauth2SessionsArgs = { - after?: InputMaybe; - before?: InputMaybe; - client?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - lastActive?: InputMaybe; - state?: InputMaybe; -}; - - -/** A user is an individual's account. */ -export type UserUpstreamOauth2LinksArgs = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; -}; - -/** A parsed user agent string */ -export type UserAgent = { - __typename?: 'UserAgent'; - /** The device type */ - deviceType: DeviceType; - /** The device model */ - model?: Maybe; - /** The name of the browser */ - name?: Maybe; - /** The operating system name */ - os?: Maybe; - /** The operating system version */ - osVersion?: Maybe; - /** The user agent string */ - raw: Scalars['String']['output']; - /** The version of the browser */ - version?: Maybe; -}; - -export type UserConnection = { - __typename?: 'UserConnection'; - /** A list of edges. */ - edges: Array; - /** A list of nodes. */ - nodes: Array; - /** Information to aid in pagination. */ - pageInfo: PageInfo; - /** Identifies the total count of items in the connection. */ - totalCount: Scalars['Int']['output']; -}; - -/** An edge in a connection. */ -export type UserEdge = { - __typename?: 'UserEdge'; - /** A cursor for use in pagination */ - cursor: Scalars['String']['output']; - /** The item at the end of the edge */ - node: User; -}; - -/** A user email address */ -export type UserEmail = CreationEvent & Node & { - __typename?: 'UserEmail'; - /** - * When the email address was confirmed. Is `null` if the email was never - * verified by the user. - */ - confirmedAt?: Maybe; - /** When the object was created. */ - createdAt: Scalars['DateTime']['output']; - /** Email address */ - email: Scalars['String']['output']; - /** ID of the object. */ - id: Scalars['ID']['output']; -}; - -export type UserEmailConnection = { - __typename?: 'UserEmailConnection'; - /** A list of edges. */ - edges: Array; - /** A list of nodes. */ - nodes: Array; - /** Information to aid in pagination. */ - pageInfo: PageInfo; - /** Identifies the total count of items in the connection. */ - totalCount: Scalars['Int']['output']; -}; - -/** An edge in a connection. */ -export type UserEmailEdge = { - __typename?: 'UserEmailEdge'; - /** A cursor for use in pagination */ - cursor: Scalars['String']['output']; - /** The item at the end of the edge */ - node: UserEmail; -}; - /** The state of a compatibility session. */ export enum UserEmailState { /** The email address has been confirmed. */ @@ -1411,17 +378,6 @@ export type VerifyEmailInput = { userEmailId: Scalars['ID']['input']; }; -/** The payload of the `verifyEmail` mutation */ -export type VerifyEmailPayload = { - __typename?: 'VerifyEmailPayload'; - /** The email address that was verified */ - email?: Maybe; - /** Status of the operation */ - status: VerifyEmailStatus; - /** The user to whom the email address belongs */ - user?: Maybe; -}; - /** The status of the `verifyEmail` mutation */ export enum VerifyEmailStatus { /** The email address was already verified before */ @@ -1432,12 +388,6 @@ export enum VerifyEmailStatus { Verified = 'VERIFIED' } -/** Represents the current viewer */ -export type Viewer = Anonymous | User; - -/** Represents the current viewer's session */ -export type ViewerSession = Anonymous | BrowserSession | Oauth2Session; - export type PasswordChange_SiteConfigFragment = { __typename?: 'SiteConfig', id: string, passwordChangeAllowed: boolean } & { ' $fragmentName'?: 'PasswordChange_SiteConfigFragment' }; export type BrowserSession_SessionFragment = { __typename?: 'BrowserSession', id: string, createdAt: string, finishedAt?: string | null, lastActiveIp?: string | null, lastActiveAt?: string | null, userAgent?: { __typename?: 'UserAgent', raw: string, name?: string | null, os?: string | null, model?: string | null, deviceType: DeviceType } | null, lastAuthentication?: { __typename?: 'Authentication', id: string, createdAt: string } | null } & { ' $fragmentName'?: 'BrowserSession_SessionFragment' };