From 096386e9b9ba7c6cd1fb298ec8b5ab72a8466811 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Mon, 28 Aug 2023 12:31:17 +0200 Subject: [PATCH] Save the application_type and the contacts in the OAuth 2.0 clients This also removes the dedicated "redirect_uris" table and makes it a field of the "oauth2_clients" table --- Cargo.toml | 1 + crates/cli/src/commands/config.rs | 3 - crates/data-model/src/oauth2/client.rs | 6 +- crates/graphql/src/model/oauth.rs | 25 ++- crates/handlers/src/graphql/tests.rs | 1 + crates/handlers/src/oauth2/registration.rs | 1 + ...a8e9e5723e4749d339465492b68e072c3a802.json | 29 ---- ...7964c454252b9b111a57c9106cfc3fdc71b8a.json | 31 ++++ ...a592f5f55f4898c6e39eb0d7c1b9cef7ca63.json} | 48 +++--- ...7be602e2fbc34a5582f31f95b0cc5fa2aeb92.json | 20 --- ...02f5065f0ae831bc8f3c66b5be4a47b37467e.json | 21 +++ ...fcb4e3d50c90accdaf22ca1cfeefdc9734300.json | 16 -- ...40bf66649742374e3643f1902ae69377fc9b6.json | 14 -- ...e17a5c5e4105d01184a38d12607d853df802.json} | 48 +++--- ...daf1407b1b3619e3a05c70b5fe9ccf9aa7b5.json} | 48 +++--- ...30828085439_oauth2_clients_more_fields.sql | 32 ++++ crates/storage-pg/src/oauth2/client.rs | 152 +++++------------- crates/storage-pg/src/oauth2/mod.rs | 3 + crates/storage/src/oauth2/client.rs | 11 +- frontend/schema.graphql | 22 +++ frontend/src/gql/graphql.ts | 12 ++ frontend/src/gql/schema.ts | 25 +++ 22 files changed, 312 insertions(+), 257 deletions(-) delete mode 100644 crates/storage-pg/.sqlx/query-31cbbd841029812c6d3500cae04a8e9e5723e4749d339465492b68e072c3a802.json create mode 100644 crates/storage-pg/.sqlx/query-34fbe0f0485a9c4060399509f087964c454252b9b111a57c9106cfc3fdc71b8a.json rename crates/storage-pg/.sqlx/{query-85499663f1adc7b7439592063f06914089f6243126a177b365bde37db5f6b33d.json => query-35734c4b54d2f1b2c311806af3e9a592f5f55f4898c6e39eb0d7c1b9cef7ca63.json} (64%) delete mode 100644 crates/storage-pg/.sqlx/query-68c4cd463e4035ba8384f11818b7be602e2fbc34a5582f31f95b0cc5fa2aeb92.json create mode 100644 crates/storage-pg/.sqlx/query-73ea17a71d62bf96f7811b7f57802f5065f0ae831bc8f3c66b5be4a47b37467e.json delete mode 100644 crates/storage-pg/.sqlx/query-7be139553610ace03193a99fe27fcb4e3d50c90accdaf22ca1cfeefdc9734300.json delete mode 100644 crates/storage-pg/.sqlx/query-7cd0264707100f5b3cb2582f3f840bf66649742374e3643f1902ae69377fc9b6.json rename crates/storage-pg/.sqlx/{query-7e676491b077d4bc8a9cdb4a27ebf119d98cd35ebb52b1064fdb2d9eed78d0e8.json => query-b2b71d12c3a4a7436bbe961c6c57e17a5c5e4105d01184a38d12607d853df802.json} (64%) rename crates/storage-pg/.sqlx/{query-db90cbc406a399f5447bd2c1d8018464f83b927dec620353516c0285b76fcf24.json => query-e6250ea5c861cd7568999fd8f490daf1407b1b3619e3a05c70b5fe9ccf9aa7b5.json} (64%) create mode 100644 crates/storage-pg/migrations/20230828085439_oauth2_clients_more_fields.sql diff --git a/Cargo.toml b/Cargo.toml index a287d42e..02813bf0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] default-members = ["crates/cli"] members = ["crates/*"] +resolver = "2" [workspace.dependencies] diff --git a/crates/cli/src/commands/config.rs b/crates/cli/src/commands/config.rs index dea8b6de..dff499fa 100644 --- a/crates/cli/src/commands/config.rs +++ b/crates/cli/src/commands/config.rs @@ -140,7 +140,6 @@ impl Options { #[tracing::instrument(name = "cli.config.sync", skip(root), err(Debug))] async fn sync(root: &super::Options, prune: bool, dry_run: bool) -> anyhow::Result<()> { // XXX: we should disallow SeedableRng::from_entropy - let mut rng = rand_chacha::ChaChaRng::from_entropy(); let clock = SystemClock::default(); let config: SyncConfig = root.load_config()?; @@ -282,8 +281,6 @@ async fn sync(root: &super::Options, prune: bool, dry_run: bool) -> anyhow::Resu repo.oauth2_client() .upsert_static( - &mut rng, - &clock, client.client_id, client_auth_method, encrypted_client_secret, diff --git a/crates/data-model/src/oauth2/client.rs b/crates/data-model/src/oauth2/client.rs index a5539a64..01b05a90 100644 --- a/crates/data-model/src/oauth2/client.rs +++ b/crates/data-model/src/oauth2/client.rs @@ -18,7 +18,7 @@ use mas_iana::{ oauth::{OAuthAuthorizationEndpointResponseType, OAuthClientAuthenticationMethod}, }; use mas_jose::jwk::PublicJsonWebKeySet; -use oauth2_types::requests::GrantType; +use oauth2_types::{oidc::ApplicationType, requests::GrantType}; use rand::RngCore; use serde::Serialize; use thiserror::Error; @@ -44,6 +44,8 @@ pub struct Client { pub encrypted_client_secret: Option, + pub application_type: Option, + /// Array of Redirection URI values used by the Client pub redirect_uris: Vec, @@ -130,6 +132,7 @@ impl Client { id: Ulid::from_datetime_with_source(now.into(), rng), client_id: "client1".to_owned(), encrypted_client_secret: None, + application_type: Some(ApplicationType::Web), redirect_uris: vec![ Url::parse("https://client1.example.com/redirect").unwrap(), Url::parse("https://client1.example.com/redirect2").unwrap(), @@ -156,6 +159,7 @@ impl Client { id: Ulid::from_datetime_with_source(now.into(), rng), client_id: "client2".to_owned(), encrypted_client_secret: None, + application_type: Some(ApplicationType::Native), redirect_uris: vec![Url::parse("https://client2.example.com/redirect").unwrap()], response_types: vec![OAuthAuthorizationEndpointResponseType::Code], grant_types: vec![GrantType::AuthorizationCode, GrantType::RefreshToken], diff --git a/crates/graphql/src/model/oauth.rs b/crates/graphql/src/model/oauth.rs index 57c94986..d385c2a3 100644 --- a/crates/graphql/src/model/oauth.rs +++ b/crates/graphql/src/model/oauth.rs @@ -17,7 +17,7 @@ use async_graphql::{Context, Description, Enum, Object, ID}; use chrono::{DateTime, Utc}; use mas_data_model::SessionState; use mas_storage::{oauth2::OAuth2ClientRepository, user::BrowserSessionRepository}; -use oauth2_types::scope::Scope; +use oauth2_types::{oidc::ApplicationType, scope::Scope}; use ulid::Ulid; use url::Url; @@ -110,6 +110,16 @@ impl OAuth2Session { } } +/// The application type advertised by the client. +#[derive(Enum, Copy, Clone, Eq, PartialEq)] +pub enum OAuth2ApplicationType { + /// Client is a web application. + Web, + + /// Client is a native application. + Native, +} + /// An OAuth 2.0 client #[derive(Description)] pub struct OAuth2Client(pub mas_data_model::Client); @@ -150,6 +160,19 @@ impl OAuth2Client { pub async fn redirect_uris(&self) -> &[Url] { &self.0.redirect_uris } + + /// List of contacts advertised by the client. + pub async fn contacts(&self) -> &[String] { + &self.0.contacts + } + + /// The application type advertised by the client. + pub async fn application_type(&self) -> Option { + match self.0.application_type? { + ApplicationType::Web => Some(OAuth2ApplicationType::Web), + ApplicationType::Native => Some(OAuth2ApplicationType::Native), + } + } } /// An OAuth 2.0 consent represents the scope a user consented to grant to a diff --git a/crates/handlers/src/graphql/tests.rs b/crates/handlers/src/graphql/tests.rs index dbd43c01..b458c016 100644 --- a/crates/handlers/src/graphql/tests.rs +++ b/crates/handlers/src/graphql/tests.rs @@ -33,6 +33,7 @@ async fn create_test_client(state: &TestState) -> Client { &state.clock, vec![], None, + None, vec![], vec![], None, diff --git a/crates/handlers/src/oauth2/registration.rs b/crates/handlers/src/oauth2/registration.rs index 40608374..e7859fb1 100644 --- a/crates/handlers/src/oauth2/registration.rs +++ b/crates/handlers/src/oauth2/registration.rs @@ -175,6 +175,7 @@ pub(crate) async fn post( &clock, metadata.redirect_uris().to_vec(), encrypted_client_secret, + metadata.application_type, //&metadata.response_types(), metadata.grant_types().to_vec(), metadata.contacts.clone().unwrap_or_default(), diff --git a/crates/storage-pg/.sqlx/query-31cbbd841029812c6d3500cae04a8e9e5723e4749d339465492b68e072c3a802.json b/crates/storage-pg/.sqlx/query-31cbbd841029812c6d3500cae04a8e9e5723e4749d339465492b68e072c3a802.json deleted file mode 100644 index 6954781a..00000000 --- a/crates/storage-pg/.sqlx/query-31cbbd841029812c6d3500cae04a8e9e5723e4749d339465492b68e072c3a802.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO oauth2_clients\n ( oauth2_client_id\n , encrypted_client_secret\n , grant_type_authorization_code\n , grant_type_refresh_token\n , client_name\n , logo_uri\n , client_uri\n , policy_uri\n , tos_uri\n , jwks_uri\n , jwks\n , id_token_signed_response_alg\n , userinfo_signed_response_alg\n , token_endpoint_auth_method\n , token_endpoint_auth_signing_alg\n , initiate_login_uri\n , is_static\n )\n VALUES\n ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, FALSE)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Text", - "Bool", - "Bool", - "Text", - "Text", - "Text", - "Text", - "Text", - "Text", - "Jsonb", - "Text", - "Text", - "Text", - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "31cbbd841029812c6d3500cae04a8e9e5723e4749d339465492b68e072c3a802" -} diff --git a/crates/storage-pg/.sqlx/query-34fbe0f0485a9c4060399509f087964c454252b9b111a57c9106cfc3fdc71b8a.json b/crates/storage-pg/.sqlx/query-34fbe0f0485a9c4060399509f087964c454252b9b111a57c9106cfc3fdc71b8a.json new file mode 100644 index 00000000..53be759a --- /dev/null +++ b/crates/storage-pg/.sqlx/query-34fbe0f0485a9c4060399509f087964c454252b9b111a57c9106cfc3fdc71b8a.json @@ -0,0 +1,31 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO oauth2_clients\n ( oauth2_client_id\n , encrypted_client_secret\n , application_type\n , redirect_uris\n , grant_type_authorization_code\n , grant_type_refresh_token\n , client_name\n , logo_uri\n , client_uri\n , policy_uri\n , tos_uri\n , jwks_uri\n , jwks\n , id_token_signed_response_alg\n , userinfo_signed_response_alg\n , token_endpoint_auth_method\n , token_endpoint_auth_signing_alg\n , initiate_login_uri\n , is_static\n )\n VALUES\n ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, FALSE)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Text", + "Text", + "TextArray", + "Bool", + "Bool", + "Text", + "Text", + "Text", + "Text", + "Text", + "Text", + "Jsonb", + "Text", + "Text", + "Text", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "34fbe0f0485a9c4060399509f087964c454252b9b111a57c9106cfc3fdc71b8a" +} diff --git a/crates/storage-pg/.sqlx/query-85499663f1adc7b7439592063f06914089f6243126a177b365bde37db5f6b33d.json b/crates/storage-pg/.sqlx/query-35734c4b54d2f1b2c311806af3e9a592f5f55f4898c6e39eb0d7c1b9cef7ca63.json similarity index 64% rename from crates/storage-pg/.sqlx/query-85499663f1adc7b7439592063f06914089f6243126a177b365bde37db5f6b33d.json rename to crates/storage-pg/.sqlx/query-35734c4b54d2f1b2c311806af3e9a592f5f55f4898c6e39eb0d7c1b9cef7ca63.json index e3d1c855..5e6e2241 100644 --- a/crates/storage-pg/.sqlx/query-85499663f1adc7b7439592063f06914089f6243126a177b365bde37db5f6b33d.json +++ b/crates/storage-pg/.sqlx/query-35734c4b54d2f1b2c311806af3e9a592f5f55f4898c6e39eb0d7c1b9cef7ca63.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT oauth2_client_id\n , encrypted_client_secret\n , ARRAY(\n SELECT redirect_uri\n FROM oauth2_client_redirect_uris r\n WHERE r.oauth2_client_id = c.oauth2_client_id\n ) AS \"redirect_uris!\"\n , grant_type_authorization_code\n , grant_type_refresh_token\n , client_name\n , logo_uri\n , client_uri\n , policy_uri\n , tos_uri\n , jwks_uri\n , jwks\n , id_token_signed_response_alg\n , userinfo_signed_response_alg\n , token_endpoint_auth_method\n , token_endpoint_auth_signing_alg\n , initiate_login_uri\n FROM oauth2_clients c\n\n WHERE oauth2_client_id = ANY($1::uuid[])\n ", + "query": "\n SELECT oauth2_client_id\n , encrypted_client_secret\n , application_type\n , redirect_uris\n , grant_type_authorization_code\n , grant_type_refresh_token\n , contacts\n , client_name\n , logo_uri\n , client_uri\n , policy_uri\n , tos_uri\n , jwks_uri\n , jwks\n , id_token_signed_response_alg\n , userinfo_signed_response_alg\n , token_endpoint_auth_method\n , token_endpoint_auth_signing_alg\n , initiate_login_uri\n FROM oauth2_clients c\n\n WHERE oauth2_client_id = ANY($1::uuid[])\n ", "describe": { "columns": [ { @@ -15,76 +15,86 @@ }, { "ordinal": 2, - "name": "redirect_uris!", - "type_info": "TextArray" + "name": "application_type", + "type_info": "Text" }, { "ordinal": 3, + "name": "redirect_uris", + "type_info": "TextArray" + }, + { + "ordinal": 4, "name": "grant_type_authorization_code", "type_info": "Bool" }, { - "ordinal": 4, + "ordinal": 5, "name": "grant_type_refresh_token", "type_info": "Bool" }, { - "ordinal": 5, + "ordinal": 6, + "name": "contacts", + "type_info": "TextArray" + }, + { + "ordinal": 7, "name": "client_name", "type_info": "Text" }, { - "ordinal": 6, + "ordinal": 8, "name": "logo_uri", "type_info": "Text" }, { - "ordinal": 7, + "ordinal": 9, "name": "client_uri", "type_info": "Text" }, { - "ordinal": 8, + "ordinal": 10, "name": "policy_uri", "type_info": "Text" }, { - "ordinal": 9, + "ordinal": 11, "name": "tos_uri", "type_info": "Text" }, { - "ordinal": 10, + "ordinal": 12, "name": "jwks_uri", "type_info": "Text" }, { - "ordinal": 11, + "ordinal": 13, "name": "jwks", "type_info": "Jsonb" }, { - "ordinal": 12, + "ordinal": 14, "name": "id_token_signed_response_alg", "type_info": "Text" }, { - "ordinal": 13, + "ordinal": 15, "name": "userinfo_signed_response_alg", "type_info": "Text" }, { - "ordinal": 14, + "ordinal": 16, "name": "token_endpoint_auth_method", "type_info": "Text" }, { - "ordinal": 15, + "ordinal": 17, "name": "token_endpoint_auth_signing_alg", "type_info": "Text" }, { - "ordinal": 16, + "ordinal": 18, "name": "initiate_login_uri", "type_info": "Text" } @@ -97,7 +107,9 @@ "nullable": [ false, true, - null, + true, + false, + false, false, false, true, @@ -114,5 +126,5 @@ true ] }, - "hash": "85499663f1adc7b7439592063f06914089f6243126a177b365bde37db5f6b33d" + "hash": "35734c4b54d2f1b2c311806af3e9a592f5f55f4898c6e39eb0d7c1b9cef7ca63" } diff --git a/crates/storage-pg/.sqlx/query-68c4cd463e4035ba8384f11818b7be602e2fbc34a5582f31f95b0cc5fa2aeb92.json b/crates/storage-pg/.sqlx/query-68c4cd463e4035ba8384f11818b7be602e2fbc34a5582f31f95b0cc5fa2aeb92.json deleted file mode 100644 index d278e6a2..00000000 --- a/crates/storage-pg/.sqlx/query-68c4cd463e4035ba8384f11818b7be602e2fbc34a5582f31f95b0cc5fa2aeb92.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO oauth2_clients\n ( oauth2_client_id\n , encrypted_client_secret\n , grant_type_authorization_code\n , grant_type_refresh_token\n , token_endpoint_auth_method\n , jwks\n , jwks_uri\n , is_static\n )\n VALUES\n ($1, $2, $3, $4, $5, $6, $7, TRUE)\n ON CONFLICT (oauth2_client_id)\n DO\n UPDATE SET encrypted_client_secret = EXCLUDED.encrypted_client_secret\n , grant_type_authorization_code = EXCLUDED.grant_type_authorization_code\n , grant_type_refresh_token = EXCLUDED.grant_type_refresh_token\n , token_endpoint_auth_method = EXCLUDED.token_endpoint_auth_method\n , jwks = EXCLUDED.jwks\n , jwks_uri = EXCLUDED.jwks_uri\n , is_static = TRUE\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Text", - "Bool", - "Bool", - "Text", - "Jsonb", - "Text" - ] - }, - "nullable": [] - }, - "hash": "68c4cd463e4035ba8384f11818b7be602e2fbc34a5582f31f95b0cc5fa2aeb92" -} diff --git a/crates/storage-pg/.sqlx/query-73ea17a71d62bf96f7811b7f57802f5065f0ae831bc8f3c66b5be4a47b37467e.json b/crates/storage-pg/.sqlx/query-73ea17a71d62bf96f7811b7f57802f5065f0ae831bc8f3c66b5be4a47b37467e.json new file mode 100644 index 00000000..54b14b77 --- /dev/null +++ b/crates/storage-pg/.sqlx/query-73ea17a71d62bf96f7811b7f57802f5065f0ae831bc8f3c66b5be4a47b37467e.json @@ -0,0 +1,21 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO oauth2_clients\n ( oauth2_client_id\n , encrypted_client_secret\n , redirect_uris\n , grant_type_authorization_code\n , grant_type_refresh_token\n , token_endpoint_auth_method\n , jwks\n , jwks_uri\n , is_static\n )\n VALUES\n ($1, $2, $3, $4, $5, $6, $7, $8, TRUE)\n ON CONFLICT (oauth2_client_id)\n DO\n UPDATE SET encrypted_client_secret = EXCLUDED.encrypted_client_secret\n , grant_type_authorization_code = EXCLUDED.grant_type_authorization_code\n , grant_type_refresh_token = EXCLUDED.grant_type_refresh_token\n , token_endpoint_auth_method = EXCLUDED.token_endpoint_auth_method\n , jwks = EXCLUDED.jwks\n , jwks_uri = EXCLUDED.jwks_uri\n , is_static = TRUE\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Text", + "TextArray", + "Bool", + "Bool", + "Text", + "Jsonb", + "Text" + ] + }, + "nullable": [] + }, + "hash": "73ea17a71d62bf96f7811b7f57802f5065f0ae831bc8f3c66b5be4a47b37467e" +} diff --git a/crates/storage-pg/.sqlx/query-7be139553610ace03193a99fe27fcb4e3d50c90accdaf22ca1cfeefdc9734300.json b/crates/storage-pg/.sqlx/query-7be139553610ace03193a99fe27fcb4e3d50c90accdaf22ca1cfeefdc9734300.json deleted file mode 100644 index 9236607a..00000000 --- a/crates/storage-pg/.sqlx/query-7be139553610ace03193a99fe27fcb4e3d50c90accdaf22ca1cfeefdc9734300.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO oauth2_client_redirect_uris\n (oauth2_client_redirect_uri_id, oauth2_client_id, redirect_uri)\n SELECT id, $2, redirect_uri\n FROM UNNEST($1::uuid[], $3::text[]) r(id, redirect_uri)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "UuidArray", - "Uuid", - "TextArray" - ] - }, - "nullable": [] - }, - "hash": "7be139553610ace03193a99fe27fcb4e3d50c90accdaf22ca1cfeefdc9734300" -} diff --git a/crates/storage-pg/.sqlx/query-7cd0264707100f5b3cb2582f3f840bf66649742374e3643f1902ae69377fc9b6.json b/crates/storage-pg/.sqlx/query-7cd0264707100f5b3cb2582f3f840bf66649742374e3643f1902ae69377fc9b6.json deleted file mode 100644 index 2f590fe1..00000000 --- a/crates/storage-pg/.sqlx/query-7cd0264707100f5b3cb2582f3f840bf66649742374e3643f1902ae69377fc9b6.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n DELETE FROM oauth2_client_redirect_uris\n WHERE oauth2_client_id = $1\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "7cd0264707100f5b3cb2582f3f840bf66649742374e3643f1902ae69377fc9b6" -} diff --git a/crates/storage-pg/.sqlx/query-7e676491b077d4bc8a9cdb4a27ebf119d98cd35ebb52b1064fdb2d9eed78d0e8.json b/crates/storage-pg/.sqlx/query-b2b71d12c3a4a7436bbe961c6c57e17a5c5e4105d01184a38d12607d853df802.json similarity index 64% rename from crates/storage-pg/.sqlx/query-7e676491b077d4bc8a9cdb4a27ebf119d98cd35ebb52b1064fdb2d9eed78d0e8.json rename to crates/storage-pg/.sqlx/query-b2b71d12c3a4a7436bbe961c6c57e17a5c5e4105d01184a38d12607d853df802.json index 65e997b5..7217ed0c 100644 --- a/crates/storage-pg/.sqlx/query-7e676491b077d4bc8a9cdb4a27ebf119d98cd35ebb52b1064fdb2d9eed78d0e8.json +++ b/crates/storage-pg/.sqlx/query-b2b71d12c3a4a7436bbe961c6c57e17a5c5e4105d01184a38d12607d853df802.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT oauth2_client_id\n , encrypted_client_secret\n , ARRAY(\n SELECT redirect_uri\n FROM oauth2_client_redirect_uris r\n WHERE r.oauth2_client_id = c.oauth2_client_id\n ) AS \"redirect_uris!\"\n , grant_type_authorization_code\n , grant_type_refresh_token\n , client_name\n , logo_uri\n , client_uri\n , policy_uri\n , tos_uri\n , jwks_uri\n , jwks\n , id_token_signed_response_alg\n , userinfo_signed_response_alg\n , token_endpoint_auth_method\n , token_endpoint_auth_signing_alg\n , initiate_login_uri\n FROM oauth2_clients c\n WHERE is_static = TRUE\n ", + "query": "\n SELECT oauth2_client_id\n , encrypted_client_secret\n , application_type\n , redirect_uris\n , grant_type_authorization_code\n , grant_type_refresh_token\n , contacts\n , client_name\n , logo_uri\n , client_uri\n , policy_uri\n , tos_uri\n , jwks_uri\n , jwks\n , id_token_signed_response_alg\n , userinfo_signed_response_alg\n , token_endpoint_auth_method\n , token_endpoint_auth_signing_alg\n , initiate_login_uri\n FROM oauth2_clients c\n WHERE is_static = TRUE\n ", "describe": { "columns": [ { @@ -15,76 +15,86 @@ }, { "ordinal": 2, - "name": "redirect_uris!", - "type_info": "TextArray" + "name": "application_type", + "type_info": "Text" }, { "ordinal": 3, + "name": "redirect_uris", + "type_info": "TextArray" + }, + { + "ordinal": 4, "name": "grant_type_authorization_code", "type_info": "Bool" }, { - "ordinal": 4, + "ordinal": 5, "name": "grant_type_refresh_token", "type_info": "Bool" }, { - "ordinal": 5, + "ordinal": 6, + "name": "contacts", + "type_info": "TextArray" + }, + { + "ordinal": 7, "name": "client_name", "type_info": "Text" }, { - "ordinal": 6, + "ordinal": 8, "name": "logo_uri", "type_info": "Text" }, { - "ordinal": 7, + "ordinal": 9, "name": "client_uri", "type_info": "Text" }, { - "ordinal": 8, + "ordinal": 10, "name": "policy_uri", "type_info": "Text" }, { - "ordinal": 9, + "ordinal": 11, "name": "tos_uri", "type_info": "Text" }, { - "ordinal": 10, + "ordinal": 12, "name": "jwks_uri", "type_info": "Text" }, { - "ordinal": 11, + "ordinal": 13, "name": "jwks", "type_info": "Jsonb" }, { - "ordinal": 12, + "ordinal": 14, "name": "id_token_signed_response_alg", "type_info": "Text" }, { - "ordinal": 13, + "ordinal": 15, "name": "userinfo_signed_response_alg", "type_info": "Text" }, { - "ordinal": 14, + "ordinal": 16, "name": "token_endpoint_auth_method", "type_info": "Text" }, { - "ordinal": 15, + "ordinal": 17, "name": "token_endpoint_auth_signing_alg", "type_info": "Text" }, { - "ordinal": 16, + "ordinal": 18, "name": "initiate_login_uri", "type_info": "Text" } @@ -95,7 +105,9 @@ "nullable": [ false, true, - null, + true, + false, + false, false, false, true, @@ -112,5 +124,5 @@ true ] }, - "hash": "7e676491b077d4bc8a9cdb4a27ebf119d98cd35ebb52b1064fdb2d9eed78d0e8" + "hash": "b2b71d12c3a4a7436bbe961c6c57e17a5c5e4105d01184a38d12607d853df802" } diff --git a/crates/storage-pg/.sqlx/query-db90cbc406a399f5447bd2c1d8018464f83b927dec620353516c0285b76fcf24.json b/crates/storage-pg/.sqlx/query-e6250ea5c861cd7568999fd8f490daf1407b1b3619e3a05c70b5fe9ccf9aa7b5.json similarity index 64% rename from crates/storage-pg/.sqlx/query-db90cbc406a399f5447bd2c1d8018464f83b927dec620353516c0285b76fcf24.json rename to crates/storage-pg/.sqlx/query-e6250ea5c861cd7568999fd8f490daf1407b1b3619e3a05c70b5fe9ccf9aa7b5.json index 57ad2759..afcca210 100644 --- a/crates/storage-pg/.sqlx/query-db90cbc406a399f5447bd2c1d8018464f83b927dec620353516c0285b76fcf24.json +++ b/crates/storage-pg/.sqlx/query-e6250ea5c861cd7568999fd8f490daf1407b1b3619e3a05c70b5fe9ccf9aa7b5.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT oauth2_client_id\n , encrypted_client_secret\n , ARRAY(\n SELECT redirect_uri\n FROM oauth2_client_redirect_uris r\n WHERE r.oauth2_client_id = c.oauth2_client_id\n ) AS \"redirect_uris!\"\n , grant_type_authorization_code\n , grant_type_refresh_token\n , client_name\n , logo_uri\n , client_uri\n , policy_uri\n , tos_uri\n , jwks_uri\n , jwks\n , id_token_signed_response_alg\n , userinfo_signed_response_alg\n , token_endpoint_auth_method\n , token_endpoint_auth_signing_alg\n , initiate_login_uri\n FROM oauth2_clients c\n\n WHERE oauth2_client_id = $1\n ", + "query": "\n SELECT oauth2_client_id\n , encrypted_client_secret\n , application_type\n , redirect_uris\n , grant_type_authorization_code\n , grant_type_refresh_token\n , contacts\n , client_name\n , logo_uri\n , client_uri\n , policy_uri\n , tos_uri\n , jwks_uri\n , jwks\n , id_token_signed_response_alg\n , userinfo_signed_response_alg\n , token_endpoint_auth_method\n , token_endpoint_auth_signing_alg\n , initiate_login_uri\n FROM oauth2_clients c\n\n WHERE oauth2_client_id = $1\n ", "describe": { "columns": [ { @@ -15,76 +15,86 @@ }, { "ordinal": 2, - "name": "redirect_uris!", - "type_info": "TextArray" + "name": "application_type", + "type_info": "Text" }, { "ordinal": 3, + "name": "redirect_uris", + "type_info": "TextArray" + }, + { + "ordinal": 4, "name": "grant_type_authorization_code", "type_info": "Bool" }, { - "ordinal": 4, + "ordinal": 5, "name": "grant_type_refresh_token", "type_info": "Bool" }, { - "ordinal": 5, + "ordinal": 6, + "name": "contacts", + "type_info": "TextArray" + }, + { + "ordinal": 7, "name": "client_name", "type_info": "Text" }, { - "ordinal": 6, + "ordinal": 8, "name": "logo_uri", "type_info": "Text" }, { - "ordinal": 7, + "ordinal": 9, "name": "client_uri", "type_info": "Text" }, { - "ordinal": 8, + "ordinal": 10, "name": "policy_uri", "type_info": "Text" }, { - "ordinal": 9, + "ordinal": 11, "name": "tos_uri", "type_info": "Text" }, { - "ordinal": 10, + "ordinal": 12, "name": "jwks_uri", "type_info": "Text" }, { - "ordinal": 11, + "ordinal": 13, "name": "jwks", "type_info": "Jsonb" }, { - "ordinal": 12, + "ordinal": 14, "name": "id_token_signed_response_alg", "type_info": "Text" }, { - "ordinal": 13, + "ordinal": 15, "name": "userinfo_signed_response_alg", "type_info": "Text" }, { - "ordinal": 14, + "ordinal": 16, "name": "token_endpoint_auth_method", "type_info": "Text" }, { - "ordinal": 15, + "ordinal": 17, "name": "token_endpoint_auth_signing_alg", "type_info": "Text" }, { - "ordinal": 16, + "ordinal": 18, "name": "initiate_login_uri", "type_info": "Text" } @@ -97,7 +107,9 @@ "nullable": [ false, true, - null, + true, + false, + false, false, false, true, @@ -114,5 +126,5 @@ true ] }, - "hash": "db90cbc406a399f5447bd2c1d8018464f83b927dec620353516c0285b76fcf24" + "hash": "e6250ea5c861cd7568999fd8f490daf1407b1b3619e3a05c70b5fe9ccf9aa7b5" } diff --git a/crates/storage-pg/migrations/20230828085439_oauth2_clients_more_fields.sql b/crates/storage-pg/migrations/20230828085439_oauth2_clients_more_fields.sql new file mode 100644 index 00000000..8cc7855f --- /dev/null +++ b/crates/storage-pg/migrations/20230828085439_oauth2_clients_more_fields.sql @@ -0,0 +1,32 @@ +-- Copyright 2023 The Matrix.org Foundation C.I.C. +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +-- Adds a few fields to OAuth 2.0 clients, and squash the redirect_uris in the same table + +ALTER TABLE "oauth2_clients" + ADD COLUMN "redirect_uris" TEXT[] NOT NULL DEFAULT '{}', + ADD COLUMN "application_type" TEXT, + ADD COLUMN "contacts" TEXT[] NOT NULL DEFAULT '{}'; + +-- Insert in the new `redirect_uris` column the values from the old table +UPDATE "oauth2_clients" + SET "redirect_uris" = ARRAY( + SELECT "redirect_uri" + FROM "oauth2_client_redirect_uris" + WHERE "oauth2_client_redirect_uris"."oauth2_client_id" = "oauth2_clients"."oauth2_client_id" + GROUP BY "redirect_uri" + ); + +-- Drop the old table +DROP TABLE "oauth2_client_redirect_uris"; \ No newline at end of file diff --git a/crates/storage-pg/src/oauth2/client.rs b/crates/storage-pg/src/oauth2/client.rs index c9fccba6..af3dfabd 100644 --- a/crates/storage-pg/src/oauth2/client.rs +++ b/crates/storage-pg/src/oauth2/client.rs @@ -27,6 +27,7 @@ use mas_iana::{ use mas_jose::jwk::PublicJsonWebKeySet; use mas_storage::{oauth2::OAuth2ClientRepository, Clock}; use oauth2_types::{ + oidc::ApplicationType, requests::GrantType, scope::{Scope, ScopeToken}, }; @@ -57,11 +58,12 @@ impl<'c> PgOAuth2ClientRepository<'c> { struct OAuth2ClientLookup { oauth2_client_id: Uuid, encrypted_client_secret: Option, + application_type: Option, redirect_uris: Vec, // response_types: Vec, grant_type_authorization_code: bool, grant_type_refresh_token: bool, - // contacts: Vec, + contacts: Vec, client_name: Option, logo_uri: Option, client_uri: Option, @@ -92,6 +94,17 @@ impl TryInto for OAuth2ClientLookup { .source(e) })?; + let application_type = self + .application_type + .map(|s| s.parse()) + .transpose() + .map_err(|e| { + DatabaseInconsistencyError::on("oauth2_clients") + .column("application_type") + .row(id) + .source(e) + })?; + let response_types = vec![ OAuthAuthorizationEndpointResponseType::Code, OAuthAuthorizationEndpointResponseType::IdToken, @@ -237,11 +250,11 @@ impl TryInto for OAuth2ClientLookup { id, client_id: id.to_string(), encrypted_client_secret: self.encrypted_client_secret, + application_type, redirect_uris, response_types, grant_types, - // contacts: self.contacts, - contacts: vec![], + contacts: self.contacts, client_name: self.client_name, logo_uri, client_uri, @@ -276,13 +289,11 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> { r#" SELECT oauth2_client_id , encrypted_client_secret - , ARRAY( - SELECT redirect_uri - FROM oauth2_client_redirect_uris r - WHERE r.oauth2_client_id = c.oauth2_client_id - ) AS "redirect_uris!" + , application_type + , redirect_uris , grant_type_authorization_code , grant_type_refresh_token + , contacts , client_name , logo_uri , client_uri @@ -328,13 +339,11 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> { r#" SELECT oauth2_client_id , encrypted_client_secret - , ARRAY( - SELECT redirect_uri - FROM oauth2_client_redirect_uris r - WHERE r.oauth2_client_id = c.oauth2_client_id - ) AS "redirect_uris!" + , application_type + , redirect_uris , grant_type_authorization_code , grant_type_refresh_token + , contacts , client_name , logo_uri , client_uri @@ -379,10 +388,11 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> { #[allow(clippy::too_many_lines)] async fn add( &mut self, - mut rng: &mut (dyn RngCore + Send), + rng: &mut (dyn RngCore + Send), clock: &dyn Clock, redirect_uris: Vec, encrypted_client_secret: Option, + application_type: Option, grant_types: Vec, contacts: Vec, client_name: Option, @@ -408,11 +418,15 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> { .transpose() .map_err(DatabaseError::to_invalid_operation)?; + let redirect_uris_array = redirect_uris.iter().map(Url::to_string).collect::>(); + sqlx::query!( r#" INSERT INTO oauth2_clients ( oauth2_client_id , encrypted_client_secret + , application_type + , redirect_uris , grant_type_authorization_code , grant_type_refresh_token , client_name @@ -430,10 +444,12 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> { , is_static ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, FALSE) + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, FALSE) "#, Uuid::from(id), encrypted_client_secret, + application_type.map(|a| a.to_string()), + &redirect_uris_array, grant_types.contains(&GrantType::AuthorizationCode), grant_types.contains(&GrantType::RefreshToken), client_name, @@ -459,40 +475,6 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> { .execute(&mut *self.conn) .await?; - { - let span = info_span!( - "db.oauth2_client.add.redirect_uris", - db.statement = tracing::field::Empty, - client.id = %id, - ); - - let (uri_ids, redirect_uris): (Vec, Vec) = redirect_uris - .iter() - .map(|uri| { - ( - Uuid::from(Ulid::from_datetime_with_source(now.into(), &mut rng)), - uri.as_str().to_owned(), - ) - }) - .unzip(); - - sqlx::query!( - r#" - INSERT INTO oauth2_client_redirect_uris - (oauth2_client_redirect_uri_id, oauth2_client_id, redirect_uri) - SELECT id, $2, redirect_uri - FROM UNNEST($1::uuid[], $3::text[]) r(id, redirect_uri) - "#, - &uri_ids, - Uuid::from(id), - &redirect_uris, - ) - .record(&span) - .execute(&mut *self.conn) - .instrument(span) - .await?; - } - let jwks = match (jwks, jwks_uri) { (None, None) => None, (Some(jwks), None) => Some(JwksOrJwksUri::Jwks(jwks)), @@ -504,6 +486,7 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> { id, client_id: id.to_string(), encrypted_client_secret, + application_type, redirect_uris, response_types: vec![ OAuthAuthorizationEndpointResponseType::Code, @@ -537,8 +520,6 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> { )] async fn upsert_static( &mut self, - rng: &mut (dyn RngCore + Send), - clock: &dyn Clock, client_id: Ulid, client_auth_method: OAuthClientAuthenticationMethod, encrypted_client_secret: Option, @@ -553,12 +534,14 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> { .map_err(DatabaseError::to_invalid_operation)?; let client_auth_method = client_auth_method.to_string(); + let redirect_uris_array = redirect_uris.iter().map(Url::to_string).collect::>(); sqlx::query!( r#" INSERT INTO oauth2_clients ( oauth2_client_id , encrypted_client_secret + , redirect_uris , grant_type_authorization_code , grant_type_refresh_token , token_endpoint_auth_method @@ -567,7 +550,7 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> { , is_static ) VALUES - ($1, $2, $3, $4, $5, $6, $7, TRUE) + ($1, $2, $3, $4, $5, $6, $7, $8, TRUE) ON CONFLICT (oauth2_client_id) DO UPDATE SET encrypted_client_secret = EXCLUDED.encrypted_client_secret @@ -580,6 +563,7 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> { "#, Uuid::from(client_id), encrypted_client_secret, + &redirect_uris_array, true, true, client_auth_method, @@ -590,41 +574,6 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> { .execute(&mut *self.conn) .await?; - { - let span = info_span!( - "db.oauth2_client.upsert_static.redirect_uris", - client.id = %client_id, - db.statement = tracing::field::Empty, - ); - - let now = clock.now(); - let (ids, redirect_uris): (Vec, Vec) = redirect_uris - .iter() - .map(|uri| { - ( - Uuid::from(Ulid::from_datetime_with_source(now.into(), &mut *rng)), - uri.as_str().to_owned(), - ) - }) - .unzip(); - - sqlx::query!( - r#" - INSERT INTO oauth2_client_redirect_uris - (oauth2_client_redirect_uri_id, oauth2_client_id, redirect_uri) - SELECT id, $2, redirect_uri - FROM UNNEST($1::uuid[], $3::text[]) r(id, redirect_uri) - "#, - &ids, - Uuid::from(client_id), - &redirect_uris, - ) - .record(&span) - .execute(&mut *self.conn) - .instrument(span) - .await?; - } - let jwks = match (jwks, jwks_uri) { (None, None) => None, (Some(jwks), None) => Some(JwksOrJwksUri::Jwks(jwks)), @@ -636,6 +585,7 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> { id: client_id, client_id: client_id.to_string(), encrypted_client_secret, + application_type: None, redirect_uris, response_types: vec![ OAuthAuthorizationEndpointResponseType::Code, @@ -672,13 +622,11 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> { r#" SELECT oauth2_client_id , encrypted_client_secret - , ARRAY( - SELECT redirect_uri - FROM oauth2_client_redirect_uris r - WHERE r.oauth2_client_id = c.oauth2_client_id - ) AS "redirect_uris!" + , application_type + , redirect_uris , grant_type_authorization_code , grant_type_refresh_token + , contacts , client_name , logo_uri , client_uri @@ -911,26 +859,6 @@ impl<'c> OAuth2ClientRepository for PgOAuth2ClientRepository<'c> { .await?; } - // Delete the redirect URIs - { - let span = info_span!( - "db.oauth2_client.delete_by_id.redirect_uris", - db.statement = tracing::field::Empty, - ); - - sqlx::query!( - r#" - DELETE FROM oauth2_client_redirect_uris - WHERE oauth2_client_id = $1 - "#, - Uuid::from(id), - ) - .record(&span) - .execute(&mut *self.conn) - .instrument(span) - .await?; - } - // Now delete the client itself let res = sqlx::query!( r#" diff --git a/crates/storage-pg/src/oauth2/mod.rs b/crates/storage-pg/src/oauth2/mod.rs index 453a4c4a..11c28fd4 100644 --- a/crates/storage-pg/src/oauth2/mod.rs +++ b/crates/storage-pg/src/oauth2/mod.rs @@ -73,6 +73,7 @@ mod tests { &clock, vec!["https://example.com/redirect".parse().unwrap()], None, + None, vec![GrantType::AuthorizationCode], Vec::new(), // TODO: contacts are not yet saved // vec!["contact@example.com".to_owned()], @@ -409,6 +410,7 @@ mod tests { &clock, vec!["https://first.example.com/redirect".parse().unwrap()], None, + None, vec![GrantType::AuthorizationCode], Vec::new(), // TODO: contacts are not yet saved // vec!["contact@first.example.com".to_owned()], @@ -434,6 +436,7 @@ mod tests { &clock, vec!["https://second.example.com/redirect".parse().unwrap()], None, + None, vec![GrantType::AuthorizationCode], Vec::new(), // TODO: contacts are not yet saved // vec!["contact@second.example.com".to_owned()], diff --git a/crates/storage/src/oauth2/client.rs b/crates/storage/src/oauth2/client.rs index 0ac7e38c..5d390c84 100644 --- a/crates/storage/src/oauth2/client.rs +++ b/crates/storage/src/oauth2/client.rs @@ -18,7 +18,7 @@ use async_trait::async_trait; use mas_data_model::{Client, User}; use mas_iana::{jose::JsonWebSignatureAlg, oauth::OAuthClientAuthenticationMethod}; use mas_jose::jwk::PublicJsonWebKeySet; -use oauth2_types::{requests::GrantType, scope::Scope}; +use oauth2_types::{oidc::ApplicationType, requests::GrantType, scope::Scope}; use rand_core::RngCore; use ulid::Ulid; use url::Url; @@ -80,6 +80,7 @@ pub trait OAuth2ClientRepository: Send + Sync { /// * `clock`: The clock used to generate timestamps /// * `redirect_uris`: The list of redirect URIs used by this client /// * `encrypted_client_secret`: The encrypted client secret, if any + /// * `application_type`: The application type of this client /// * `grant_types`: The list of grant types this client can use /// * `contacts`: The list of contacts for this client /// * `client_name`: The human-readable name of this client, if given @@ -110,6 +111,7 @@ pub trait OAuth2ClientRepository: Send + Sync { clock: &dyn Clock, redirect_uris: Vec, encrypted_client_secret: Option, + application_type: Option, grant_types: Vec, contacts: Vec, client_name: Option, @@ -132,8 +134,6 @@ pub trait OAuth2ClientRepository: Send + Sync { /// /// # Parameters /// - /// * `rng`: The random number generator to use - /// * `clock`: The clock used to generate timestamps /// * `client_id`: The client ID /// * `client_auth_method`: The authentication method this client uses /// * `encrypted_client_secret`: The encrypted client secret, if any @@ -147,8 +147,6 @@ pub trait OAuth2ClientRepository: Send + Sync { #[allow(clippy::too_many_arguments)] async fn upsert_static( &mut self, - rng: &mut (dyn RngCore + Send), - clock: &dyn Clock, client_id: Ulid, client_auth_method: OAuthClientAuthenticationMethod, encrypted_client_secret: Option, @@ -244,6 +242,7 @@ repository_impl!(OAuth2ClientRepository: clock: &dyn Clock, redirect_uris: Vec, encrypted_client_secret: Option, + application_type: Option, grant_types: Vec, contacts: Vec, client_name: Option, @@ -262,8 +261,6 @@ repository_impl!(OAuth2ClientRepository: async fn upsert_static( &mut self, - rng: &mut (dyn RngCore + Send), - clock: &dyn Clock, client_id: Ulid, client_auth_method: OAuthClientAuthenticationMethod, encrypted_client_secret: Option, diff --git a/frontend/schema.graphql b/frontend/schema.graphql index 1e1fefe6..e171bcd5 100644 --- a/frontend/schema.graphql +++ b/frontend/schema.graphql @@ -475,6 +475,20 @@ interface Node { id: ID! } +""" +The application type advertised by the client. +""" +enum Oauth2ApplicationType { + """ + Client is a web application. + """ + WEB + """ + Client is a native application. + """ + NATIVE +} + """ An OAuth 2.0 client """ @@ -507,6 +521,14 @@ type Oauth2Client implements Node { List of redirect URIs used for authorization grants by the client. """ redirectUris: [Url!]! + """ + List of contacts advertised by the client. + """ + contacts: [String!]! + """ + The application type advertised by the client. + """ + applicationType: Oauth2ApplicationType } """ diff --git a/frontend/src/gql/graphql.ts b/frontend/src/gql/graphql.ts index 4cb414e0..35b0c817 100644 --- a/frontend/src/gql/graphql.ts +++ b/frontend/src/gql/graphql.ts @@ -382,15 +382,27 @@ export type Node = { id: Scalars["ID"]["output"]; }; +/** The application type advertised by the client. */ +export enum Oauth2ApplicationType { + /** Client is a native application. */ + Native = "NATIVE", + /** Client is a web application. */ + 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; + /** List of contacts advertised by the client. */ + contacts: Array; /** ID of the object. */ id: Scalars["ID"]["output"]; /** Privacy policy URI advertised by the client. */ diff --git a/frontend/src/gql/schema.ts b/frontend/src/gql/schema.ts index 87e97f66..4688f0de 100644 --- a/frontend/src/gql/schema.ts +++ b/frontend/src/gql/schema.ts @@ -1046,6 +1046,14 @@ export default { kind: "OBJECT", name: "Oauth2Client", fields: [ + { + name: "applicationType", + type: { + kind: "SCALAR", + name: "Any", + }, + args: [], + }, { name: "clientId", type: { @@ -1073,6 +1081,23 @@ export default { }, args: [], }, + { + name: "contacts", + type: { + kind: "NON_NULL", + ofType: { + kind: "LIST", + ofType: { + kind: "NON_NULL", + ofType: { + kind: "SCALAR", + name: "Any", + }, + }, + }, + }, + args: [], + }, { name: "id", type: {