diff --git a/crates/data-model/src/compat.rs b/crates/data-model/src/compat.rs index d6f772db..07ff9aaa 100644 --- a/crates/data-model/src/compat.rs +++ b/crates/data-model/src/compat.rs @@ -23,8 +23,6 @@ use thiserror::Error; use ulid::Ulid; use url::Url; -use crate::User; - static DEVICE_ID_LENGTH: usize = 10; #[derive(Debug, Clone, PartialEq, Eq, Serialize)] @@ -85,7 +83,7 @@ impl TryFrom for Device { #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct CompatSession { pub id: Ulid, - pub user: User, + pub user_id: Ulid, pub device: Device, pub created_at: DateTime, pub finished_at: Option>, diff --git a/crates/data-model/src/oauth2/session.rs b/crates/data-model/src/oauth2/session.rs index ff222ca8..29454feb 100644 --- a/crates/data-model/src/oauth2/session.rs +++ b/crates/data-model/src/oauth2/session.rs @@ -16,13 +16,10 @@ use oauth2_types::scope::Scope; use serde::Serialize; use ulid::Ulid; -use super::client::Client; -use crate::users::BrowserSession; - #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct Session { pub id: Ulid, - pub browser_session: BrowserSession, - pub client: Client, + pub user_session_id: Ulid, + pub client_id: Ulid, pub scope: Scope, } diff --git a/crates/graphql/src/model/compat_sessions.rs b/crates/graphql/src/model/compat_sessions.rs index 5b272b18..f3610233 100644 --- a/crates/graphql/src/model/compat_sessions.rs +++ b/crates/graphql/src/model/compat_sessions.rs @@ -12,9 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use async_graphql::{Description, Object, ID}; +use anyhow::Context as _; +use async_graphql::{Context, Description, Object, ID}; use chrono::{DateTime, Utc}; use mas_data_model::CompatSsoLoginState; +use mas_storage::{user::UserRepository, Repository}; +use sqlx::PgPool; use url::Url; use super::{NodeType, User}; @@ -32,8 +35,14 @@ impl CompatSession { } /// The user authorized for this session. - async fn user(&self) -> User { - User(self.0.user.clone()) + async fn user(&self, ctx: &Context<'_>) -> Result { + let mut conn = ctx.data::()?.acquire().await?; + let user = conn + .user() + .lookup(self.0.user_id) + .await? + .context("Could not load user")?; + Ok(User(user)) } /// The Matrix Device ID of this session. diff --git a/crates/graphql/src/model/oauth.rs b/crates/graphql/src/model/oauth.rs index 5f1236f2..8e418e6c 100644 --- a/crates/graphql/src/model/oauth.rs +++ b/crates/graphql/src/model/oauth.rs @@ -14,7 +14,9 @@ use anyhow::Context as _; use async_graphql::{Context, Description, Object, ID}; -use mas_storage::{oauth2::client::OAuth2ClientRepository, Repository}; +use mas_storage::{ + oauth2::client::OAuth2ClientRepository, user::BrowserSessionRepository, Repository, +}; use oauth2_types::scope::Scope; use sqlx::PgPool; use ulid::Ulid; @@ -35,8 +37,15 @@ impl OAuth2Session { } /// OAuth 2.0 client used by this session. - pub async fn client(&self) -> OAuth2Client { - OAuth2Client(self.0.client.clone()) + pub async fn client(&self, ctx: &Context<'_>) -> Result { + let mut conn = ctx.data::()?.acquire().await?; + let client = conn + .oauth2_client() + .lookup(self.0.client_id) + .await? + .context("Could not load client")?; + + Ok(OAuth2Client(client)) } /// Scope granted for this session. @@ -45,13 +54,30 @@ impl OAuth2Session { } /// The browser session which started this OAuth 2.0 session. - pub async fn browser_session(&self) -> BrowserSession { - BrowserSession(self.0.browser_session.clone()) + pub async fn browser_session( + &self, + ctx: &Context<'_>, + ) -> Result { + let mut conn = ctx.data::()?.acquire().await?; + let browser_session = conn + .browser_session() + .lookup(self.0.user_session_id) + .await? + .context("Could not load browser session")?; + + Ok(BrowserSession(browser_session)) } /// User authorized for this session. - pub async fn user(&self) -> User { - User(self.0.browser_session.user.clone()) + pub async fn user(&self, ctx: &Context<'_>) -> Result { + let mut conn = ctx.data::()?.acquire().await?; + let browser_session = conn + .browser_session() + .lookup(self.0.user_session_id) + .await? + .context("Could not load browser session")?; + + Ok(User(browser_session.user)) } } diff --git a/crates/handlers/src/compat/login.rs b/crates/handlers/src/compat/login.rs index c3b91002..c59c7dd8 100644 --- a/crates/handlers/src/compat/login.rs +++ b/crates/handlers/src/compat/login.rs @@ -15,7 +15,7 @@ use axum::{extract::State, response::IntoResponse, Json}; use chrono::Duration; use hyper::StatusCode; -use mas_data_model::{CompatSession, CompatSsoLoginState, Device, TokenType}; +use mas_data_model::{CompatSession, CompatSsoLoginState, Device, TokenType, User}; use mas_storage::{ compat::{ add_compat_access_token, add_compat_refresh_token, get_compat_sso_login_by_token, @@ -197,7 +197,7 @@ pub(crate) async fn post( ) -> Result { let (clock, mut rng) = crate::clock_and_rng(); let mut txn = pool.begin().await?; - let session = match input.credentials { + let (session, user) = match input.credentials { Credentials::Password { identifier: Identifier::User { user }, password, @@ -210,7 +210,7 @@ pub(crate) async fn post( } }; - let user_id = format!("@{username}:{homeserver}", username = session.user.username); + let user_id = format!("@{username}:{homeserver}", username = user.username); // If the client asked for a refreshable token, make it expire let expires_in = if input.refresh_token { @@ -262,13 +262,13 @@ async fn token_login( txn: &mut Transaction<'_, Postgres>, clock: &Clock, token: &str, -) -> Result { +) -> Result<(CompatSession, User), RouteError> { let login = get_compat_sso_login_by_token(&mut *txn, token) .await? .ok_or(RouteError::InvalidLoginToken)?; let now = clock.now(); - match login.state { + let user_id = match login.state { CompatSsoLoginState::Pending => { tracing::error!( compat_sso_login.id = %login.id, @@ -278,11 +278,14 @@ async fn token_login( } CompatSsoLoginState::Fulfilled { fulfilled_at: fullfilled_at, + ref session, .. } => { if now > fullfilled_at + Duration::seconds(30) { return Err(RouteError::LoginTookTooLong); } + + session.user_id } CompatSsoLoginState::Exchanged { exchanged_at, .. } => { if now > exchanged_at + Duration::seconds(30) { @@ -295,12 +298,18 @@ async fn token_login( return Err(RouteError::InvalidLoginToken); } - } + }; + + let user = txn + .user() + .lookup(user_id) + .await? + .ok_or(RouteError::UserNotFound)?; let login = mark_compat_sso_login_as_exchanged(&mut *txn, clock, login).await?; match login.state { - CompatSsoLoginState::Exchanged { session, .. } => Ok(session), + CompatSsoLoginState::Exchanged { session, .. } => Ok((session, user)), _ => unreachable!(), } } @@ -310,7 +319,7 @@ async fn user_password_login( txn: &mut Transaction<'_, Postgres>, username: String, password: String, -) -> Result { +) -> Result<(CompatSession, User), RouteError> { let (clock, mut rng) = crate::clock_and_rng(); // Find the user @@ -356,7 +365,7 @@ async fn user_password_login( // Now that the user credentials have been verified, start a new compat session let device = Device::generate(&mut rng); - let session = start_compat_session(&mut *txn, &mut rng, &clock, user, device).await?; + let session = start_compat_session(&mut *txn, &mut rng, &clock, &user, device).await?; - Ok(session) + Ok((session, user)) } diff --git a/crates/handlers/src/compat/login_sso_complete.rs b/crates/handlers/src/compat/login_sso_complete.rs index 49790842..f31856d6 100644 --- a/crates/handlers/src/compat/login_sso_complete.rs +++ b/crates/handlers/src/compat/login_sso_complete.rs @@ -182,7 +182,7 @@ pub async fn post( let device = Device::generate(&mut rng); let _login = - fullfill_compat_sso_login(&mut txn, &mut rng, &clock, session.user, login, device).await?; + fullfill_compat_sso_login(&mut txn, &mut rng, &clock, &session.user, login, device).await?; txn.commit().await?; diff --git a/crates/handlers/src/oauth2/introspection.rs b/crates/handlers/src/oauth2/introspection.rs index c2e68261..71f3f148 100644 --- a/crates/handlers/src/oauth2/introspection.rs +++ b/crates/handlers/src/oauth2/introspection.rs @@ -26,7 +26,8 @@ use mas_storage::{ oauth2::{ access_token::lookup_active_access_token, refresh_token::lookup_active_refresh_token, }, - Clock, + user::{BrowserSessionRepository, UserRepository}, + Clock, Repository, }; use oauth2_types::{ errors::{ClientError, ClientErrorCode}, @@ -171,16 +172,23 @@ pub(crate) async fn post( .await? .ok_or(RouteError::UnknownToken)?; + let browser_session = conn + .browser_session() + .lookup(session.user_session_id) + .await? + // XXX: is that the right error to bubble up? + .ok_or(RouteError::UnknownToken)?; + IntrospectionResponse { active: true, scope: Some(session.scope), - client_id: Some(session.client.client_id), - username: Some(session.browser_session.user.username), + client_id: Some(session.client_id.to_string()), + username: Some(browser_session.user.username), token_type: Some(OAuthTokenTypeHint::AccessToken), exp: Some(token.expires_at), iat: Some(token.created_at), nbf: Some(token.created_at), - sub: Some(session.browser_session.user.sub), + sub: Some(browser_session.user.sub), aud: None, iss: None, jti: None, @@ -191,16 +199,23 @@ pub(crate) async fn post( .await? .ok_or(RouteError::UnknownToken)?; + let browser_session = conn + .browser_session() + .lookup(session.user_session_id) + .await? + // XXX: is that the right error to bubble up? + .ok_or(RouteError::UnknownToken)?; + IntrospectionResponse { active: true, scope: Some(session.scope), - client_id: Some(session.client.client_id), - username: Some(session.browser_session.user.username), + client_id: Some(session.client_id.to_string()), + username: Some(browser_session.user.username), token_type: Some(OAuthTokenTypeHint::RefreshToken), exp: None, iat: Some(token.created_at), nbf: Some(token.created_at), - sub: Some(session.browser_session.user.sub), + sub: Some(browser_session.user.sub), aud: None, iss: None, jti: None, @@ -211,6 +226,13 @@ pub(crate) async fn post( .await? .ok_or(RouteError::UnknownToken)?; + let user = conn + .user() + .lookup(session.user_id) + .await? + // XXX: is that the right error to bubble up? + .ok_or(RouteError::UnknownToken)?; + let device_scope = session.device.to_scope_token(); let scope = [API_SCOPE, device_scope].into_iter().collect(); @@ -218,12 +240,12 @@ pub(crate) async fn post( active: true, scope: Some(scope), client_id: Some("legacy".into()), - username: Some(session.user.username), + username: Some(user.username), token_type: Some(OAuthTokenTypeHint::AccessToken), exp: token.expires_at, iat: Some(token.created_at), nbf: Some(token.created_at), - sub: Some(session.user.sub), + sub: Some(user.sub), aud: None, iss: None, jti: None, @@ -235,6 +257,13 @@ pub(crate) async fn post( .await? .ok_or(RouteError::UnknownToken)?; + let user = conn + .user() + .lookup(session.user_id) + .await? + // XXX: is that the right error to bubble up? + .ok_or(RouteError::UnknownToken)?; + let device_scope = session.device.to_scope_token(); let scope = [API_SCOPE, device_scope].into_iter().collect(); @@ -242,12 +271,12 @@ pub(crate) async fn post( active: true, scope: Some(scope), client_id: Some("legacy".into()), - username: Some(session.user.username), + username: Some(user.username), token_type: Some(OAuthTokenTypeHint::RefreshToken), exp: None, iat: Some(refresh_token.created_at), nbf: Some(refresh_token.created_at), - sub: Some(session.user.sub), + sub: Some(user.sub), aud: None, iss: None, jti: None, diff --git a/crates/handlers/src/oauth2/token.rs b/crates/handlers/src/oauth2/token.rs index a6d899f4..473dcab8 100644 --- a/crates/handlers/src/oauth2/token.rs +++ b/crates/handlers/src/oauth2/token.rs @@ -31,11 +31,15 @@ use mas_jose::{ }; use mas_keystore::{Encrypter, Keystore}; use mas_router::UrlBuilder; -use mas_storage::oauth2::{ - access_token::{add_access_token, revoke_access_token}, - authorization_grant::{exchange_grant, lookup_grant_by_code}, - end_oauth_session, - refresh_token::{add_refresh_token, consume_refresh_token, lookup_active_refresh_token}, +use mas_storage::{ + oauth2::{ + access_token::{add_access_token, revoke_access_token}, + authorization_grant::{exchange_grant, lookup_grant_by_code}, + end_oauth_session, + refresh_token::{add_refresh_token, consume_refresh_token, lookup_active_refresh_token}, + }, + user::BrowserSessionRepository, + Repository, }; use oauth2_types::{ errors::{ClientError, ClientErrorCode}, @@ -102,12 +106,15 @@ pub(crate) enum RouteError { #[error("no suitable key found for signing")] InvalidSigningKey, + + #[error("failed to load browser session")] + NoSuchBrowserSession, } impl IntoResponse for RouteError { fn into_response(self) -> axum::response::Response { match self { - Self::Internal(_) | Self::InvalidSigningKey => ( + Self::Internal(_) | Self::InvalidSigningKey | Self::NoSuchBrowserSession => ( StatusCode::INTERNAL_SERVER_ERROR, Json(ClientError::from(ClientErrorCode::ServerError)), ), @@ -253,7 +260,7 @@ async fn authorization_code_grant( // This should never happen, since we looked up in the database using the code let code = authz_grant.code.as_ref().ok_or(RouteError::InvalidGrant)?; - if client.client_id != session.client.client_id { + if client.id != session.client_id { return Err(RouteError::UnauthorizedClient); } @@ -267,7 +274,11 @@ async fn authorization_code_grant( } }; - let browser_session = &session.browser_session; + let browser_session = txn + .browser_session() + .lookup(session.user_session_id) + .await? + .ok_or(RouteError::NoSuchBrowserSession)?; let ttl = Duration::minutes(5); let access_token_str = TokenType::AccessToken.generate(&mut rng); @@ -357,7 +368,7 @@ async fn refresh_token_grant( .await? .ok_or(RouteError::InvalidGrant)?; - if client.client_id != session.client.client_id { + if client.id != session.client_id { // As per https://datatracker.ietf.org/doc/html/rfc6749#section-5.2 return Err(RouteError::InvalidGrant); } diff --git a/crates/handlers/src/oauth2/userinfo.rs b/crates/handlers/src/oauth2/userinfo.rs index 225870ad..699b049a 100644 --- a/crates/handlers/src/oauth2/userinfo.rs +++ b/crates/handlers/src/oauth2/userinfo.rs @@ -28,7 +28,11 @@ use mas_jose::{ }; use mas_keystore::Keystore; use mas_router::UrlBuilder; -use mas_storage::{user::UserEmailRepository, Repository}; +use mas_storage::{ + oauth2::client::OAuth2ClientRepository, + user::{BrowserSessionRepository, UserEmailRepository}, + Repository, +}; use oauth2_types::scope; use serde::Serialize; use serde_with::skip_serializing_none; @@ -64,6 +68,12 @@ pub enum RouteError { #[error("no suitable key found for signing")] InvalidSigningKey, + + #[error("failed to load client")] + NoSuchClient, + + #[error("failed to load browser session")] + NoSuchBrowserSession, } impl_from_error_for_route!(sqlx::Error); @@ -74,7 +84,10 @@ impl_from_error_for_route!(mas_jose::jwt::JwtSignatureError); impl IntoResponse for RouteError { fn into_response(self) -> axum::response::Response { match self { - Self::Internal(_) | Self::InvalidSigningKey => { + Self::Internal(_) + | Self::InvalidSigningKey + | Self::NoSuchClient + | Self::NoSuchBrowserSession => { (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response() } Self::AuthorizationVerificationError(_e) => StatusCode::UNAUTHORIZED.into_response(), @@ -93,7 +106,13 @@ pub async fn get( let session = user_authorization.protected(&mut conn).await?; - let user = session.browser_session.user; + let browser_session = conn + .browser_session() + .lookup(session.user_session_id) + .await? + .ok_or(RouteError::NoSuchBrowserSession)?; + + let user = browser_session.user; let user_email = if session.scope.contains(&scope::EMAIL) { conn.user_email().get_primary(&user).await? @@ -108,7 +127,13 @@ pub async fn get( email: user_email.map(|u| u.email), }; - if let Some(alg) = session.client.userinfo_signed_response_alg { + let client = conn + .oauth2_client() + .lookup(session.client_id) + .await? + .ok_or(RouteError::NoSuchClient)?; + + if let Some(alg) = client.userinfo_signed_response_alg { let key = key_store .signing_key_for_algorithm(&alg) .ok_or(RouteError::InvalidSigningKey)?; @@ -119,7 +144,7 @@ pub async fn get( let user_info = SignedUserInfo { iss: url_builder.oidc_issuer().to_string(), - aud: session.client.client_id, + aud: client.client_id, user_info, }; diff --git a/crates/storage/sqlx-data.json b/crates/storage/sqlx-data.json index feced0ba..4784c030 100644 --- a/crates/storage/sqlx-data.json +++ b/crates/storage/sqlx-data.json @@ -1,77 +1,67 @@ { "db": "PostgreSQL", - "03bc4a14e97e011fec04e5788a967e04838cf978984254ecfd2c8b8a979da1c8": { + "021f845e564500457e2e0c8614beb1d9fd10b4b5f13515478f7ca25b5474d016": { "describe": { "columns": [ { - "name": "oauth2_access_token_id", + "name": "compat_refresh_token_id", "ordinal": 0, "type_info": "Uuid" }, { - "name": "oauth2_access_token", + "name": "compat_refresh_token", "ordinal": 1, "type_info": "Text" }, { - "name": "oauth2_access_token_created_at", + "name": "compat_refresh_token_created_at", "ordinal": 2, "type_info": "Timestamptz" }, { - "name": "oauth2_access_token_expires_at", + "name": "compat_access_token_id", "ordinal": 3, - "type_info": "Timestamptz" + "type_info": "Uuid" }, { - "name": "oauth2_session_id!", + "name": "compat_access_token", "ordinal": 4, - "type_info": "Uuid" - }, - { - "name": "oauth2_client_id!", - "ordinal": 5, - "type_info": "Uuid" - }, - { - "name": "scope!", - "ordinal": 6, "type_info": "Text" }, { - "name": "user_session_id!", + "name": "compat_access_token_created_at", + "ordinal": 5, + "type_info": "Timestamptz" + }, + { + "name": "compat_access_token_expires_at", + "ordinal": 6, + "type_info": "Timestamptz" + }, + { + "name": "compat_session_id", "ordinal": 7, "type_info": "Uuid" }, { - "name": "user_session_created_at!", + "name": "compat_session_created_at", "ordinal": 8, "type_info": "Timestamptz" }, { - "name": "user_id!", + "name": "compat_session_finished_at", "ordinal": 9, - "type_info": "Uuid" + "type_info": "Timestamptz" }, { - "name": "user_username!", + "name": "compat_session_device_id", "ordinal": 10, "type_info": "Text" }, { - "name": "user_primary_user_email_id", + "name": "user_id", "ordinal": 11, "type_info": "Uuid" - }, - { - "name": "user_session_last_authentication_id?", - "ordinal": 12, - "type_info": "Uuid" - }, - { - "name": "user_session_last_authentication_created_at?", - "ordinal": 13, - "type_info": "Timestamptz" } ], "nullable": [ @@ -81,9 +71,7 @@ false, false, false, - false, - false, - false, + true, false, false, true, @@ -96,7 +84,7 @@ ] } }, - "query": "\n SELECT at.oauth2_access_token_id\n , at.access_token AS \"oauth2_access_token\"\n , at.created_at AS \"oauth2_access_token_created_at\"\n , at.expires_at AS \"oauth2_access_token_expires_at\"\n , os.oauth2_session_id AS \"oauth2_session_id!\"\n , os.oauth2_client_id AS \"oauth2_client_id!\"\n , os.scope AS \"scope!\"\n , us.user_session_id AS \"user_session_id!\"\n , us.created_at AS \"user_session_created_at!\"\n , u.user_id AS \"user_id!\"\n , u.username AS \"user_username!\"\n , u.primary_user_email_id AS \"user_primary_user_email_id\"\n , usa.user_session_authentication_id AS \"user_session_last_authentication_id?\"\n , usa.created_at AS \"user_session_last_authentication_created_at?\"\n\n FROM oauth2_access_tokens at\n INNER JOIN oauth2_sessions os\n USING (oauth2_session_id)\n INNER JOIN user_sessions us\n USING (user_session_id)\n INNER JOIN users u\n USING (user_id)\n LEFT JOIN user_session_authentications usa\n USING (user_session_id)\n\n WHERE at.access_token = $1\n AND at.revoked_at IS NULL\n AND os.finished_at IS NULL\n\n ORDER BY usa.created_at DESC\n LIMIT 1\n " + "query": "\n SELECT cr.compat_refresh_token_id\n , cr.refresh_token AS \"compat_refresh_token\"\n , cr.created_at AS \"compat_refresh_token_created_at\"\n , ct.compat_access_token_id\n , ct.access_token AS \"compat_access_token\"\n , ct.created_at AS \"compat_access_token_created_at\"\n , ct.expires_at AS \"compat_access_token_expires_at\"\n , cs.compat_session_id\n , cs.created_at AS \"compat_session_created_at\"\n , cs.finished_at AS \"compat_session_finished_at\"\n , cs.device_id AS \"compat_session_device_id\"\n , cs.user_id\n\n FROM compat_refresh_tokens cr\n INNER JOIN compat_sessions cs\n USING (compat_session_id)\n INNER JOIN compat_access_tokens ct\n USING (compat_access_token_id)\n\n WHERE cr.refresh_token = $1\n AND cr.consumed_at IS NULL\n AND cs.finished_at IS NULL\n " }, "08d7df347c806ef14b6d0fb031cab041d79ba48528420160e23286369db7af35": { "describe": { @@ -196,438 +184,6 @@ }, "query": "\n SELECT\n upstream_oauth_provider_id,\n issuer,\n scope,\n client_id,\n encrypted_client_secret,\n token_endpoint_signing_alg,\n token_endpoint_auth_method,\n created_at\n FROM upstream_oauth_providers\n " }, - "16a1c5fe5a4c5481212560d79d589b550dfefe7480c5ee4febcbfaaa01ee93a4": { - "describe": { - "columns": [ - { - "name": "compat_sso_login_id", - "ordinal": 0, - "type_info": "Uuid" - }, - { - "name": "compat_sso_login_token", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "compat_sso_login_redirect_uri", - "ordinal": 2, - "type_info": "Text" - }, - { - "name": "compat_sso_login_created_at", - "ordinal": 3, - "type_info": "Timestamptz" - }, - { - "name": "compat_sso_login_fulfilled_at", - "ordinal": 4, - "type_info": "Timestamptz" - }, - { - "name": "compat_sso_login_exchanged_at", - "ordinal": 5, - "type_info": "Timestamptz" - }, - { - "name": "compat_session_id?", - "ordinal": 6, - "type_info": "Uuid" - }, - { - "name": "compat_session_created_at?", - "ordinal": 7, - "type_info": "Timestamptz" - }, - { - "name": "compat_session_finished_at?", - "ordinal": 8, - "type_info": "Timestamptz" - }, - { - "name": "compat_session_device_id?", - "ordinal": 9, - "type_info": "Text" - }, - { - "name": "user_id?", - "ordinal": 10, - "type_info": "Uuid" - }, - { - "name": "user_username?", - "ordinal": 11, - "type_info": "Text" - }, - { - "name": "user_primary_user_email_id?", - "ordinal": 12, - "type_info": "Uuid" - } - ], - "nullable": [ - false, - false, - false, - false, - true, - true, - false, - false, - true, - false, - false, - false, - true - ], - "parameters": { - "Left": [ - "Text" - ] - } - }, - "query": "\n SELECT\n cl.compat_sso_login_id,\n cl.login_token AS \"compat_sso_login_token\",\n cl.redirect_uri AS \"compat_sso_login_redirect_uri\",\n cl.created_at AS \"compat_sso_login_created_at\",\n cl.fulfilled_at AS \"compat_sso_login_fulfilled_at\",\n cl.exchanged_at AS \"compat_sso_login_exchanged_at\",\n cs.compat_session_id AS \"compat_session_id?\",\n cs.created_at AS \"compat_session_created_at?\",\n cs.finished_at AS \"compat_session_finished_at?\",\n cs.device_id AS \"compat_session_device_id?\",\n u.user_id AS \"user_id?\",\n u.username AS \"user_username?\",\n u.primary_user_email_id AS \"user_primary_user_email_id?\"\n FROM compat_sso_logins cl\n LEFT JOIN compat_sessions cs\n USING (compat_session_id)\n LEFT JOIN users u\n USING (user_id)\n WHERE cl.login_token = $1\n " - }, - "1a5e0d1d88065bb4e7f790942856d1d94ecdb30a7007f3277ca3f7cbdabd4dff": { - "describe": { - "columns": [ - { - "name": "oauth2_authorization_grant_id", - "ordinal": 0, - "type_info": "Uuid" - }, - { - "name": "oauth2_authorization_grant_created_at", - "ordinal": 1, - "type_info": "Timestamptz" - }, - { - "name": "oauth2_authorization_grant_cancelled_at", - "ordinal": 2, - "type_info": "Timestamptz" - }, - { - "name": "oauth2_authorization_grant_fulfilled_at", - "ordinal": 3, - "type_info": "Timestamptz" - }, - { - "name": "oauth2_authorization_grant_exchanged_at", - "ordinal": 4, - "type_info": "Timestamptz" - }, - { - "name": "oauth2_authorization_grant_scope", - "ordinal": 5, - "type_info": "Text" - }, - { - "name": "oauth2_authorization_grant_state", - "ordinal": 6, - "type_info": "Text" - }, - { - "name": "oauth2_authorization_grant_redirect_uri", - "ordinal": 7, - "type_info": "Text" - }, - { - "name": "oauth2_authorization_grant_response_mode", - "ordinal": 8, - "type_info": "Text" - }, - { - "name": "oauth2_authorization_grant_nonce", - "ordinal": 9, - "type_info": "Text" - }, - { - "name": "oauth2_authorization_grant_max_age", - "ordinal": 10, - "type_info": "Int4" - }, - { - "name": "oauth2_client_id", - "ordinal": 11, - "type_info": "Uuid" - }, - { - "name": "oauth2_authorization_grant_code", - "ordinal": 12, - "type_info": "Text" - }, - { - "name": "oauth2_authorization_grant_response_type_code", - "ordinal": 13, - "type_info": "Bool" - }, - { - "name": "oauth2_authorization_grant_response_type_id_token", - "ordinal": 14, - "type_info": "Bool" - }, - { - "name": "oauth2_authorization_grant_code_challenge", - "ordinal": 15, - "type_info": "Text" - }, - { - "name": "oauth2_authorization_grant_code_challenge_method", - "ordinal": 16, - "type_info": "Text" - }, - { - "name": "oauth2_authorization_grant_requires_consent", - "ordinal": 17, - "type_info": "Bool" - }, - { - "name": "oauth2_session_id?", - "ordinal": 18, - "type_info": "Uuid" - }, - { - "name": "user_session_id?", - "ordinal": 19, - "type_info": "Uuid" - }, - { - "name": "user_session_created_at?", - "ordinal": 20, - "type_info": "Timestamptz" - }, - { - "name": "user_id?", - "ordinal": 21, - "type_info": "Uuid" - }, - { - "name": "user_username?", - "ordinal": 22, - "type_info": "Text" - }, - { - "name": "user_primary_user_email_id?", - "ordinal": 23, - "type_info": "Uuid" - }, - { - "name": "user_session_last_authentication_id?", - "ordinal": 24, - "type_info": "Uuid" - }, - { - "name": "user_session_last_authentication_created_at?", - "ordinal": 25, - "type_info": "Timestamptz" - } - ], - "nullable": [ - false, - false, - true, - true, - true, - false, - true, - false, - false, - true, - true, - false, - true, - false, - false, - true, - true, - false, - false, - false, - false, - false, - false, - true, - false, - false - ], - "parameters": { - "Left": [ - "Text" - ] - } - }, - "query": "\n SELECT\n og.oauth2_authorization_grant_id,\n og.created_at AS oauth2_authorization_grant_created_at,\n og.cancelled_at AS oauth2_authorization_grant_cancelled_at,\n og.fulfilled_at AS oauth2_authorization_grant_fulfilled_at,\n og.exchanged_at AS oauth2_authorization_grant_exchanged_at,\n og.scope AS oauth2_authorization_grant_scope,\n og.state AS oauth2_authorization_grant_state,\n og.redirect_uri AS oauth2_authorization_grant_redirect_uri,\n og.response_mode AS oauth2_authorization_grant_response_mode,\n og.nonce AS oauth2_authorization_grant_nonce,\n og.max_age AS oauth2_authorization_grant_max_age,\n og.oauth2_client_id AS oauth2_client_id,\n og.authorization_code AS oauth2_authorization_grant_code,\n og.response_type_code AS oauth2_authorization_grant_response_type_code,\n og.response_type_id_token AS oauth2_authorization_grant_response_type_id_token,\n og.code_challenge AS oauth2_authorization_grant_code_challenge,\n og.code_challenge_method AS oauth2_authorization_grant_code_challenge_method,\n og.requires_consent AS oauth2_authorization_grant_requires_consent,\n os.oauth2_session_id AS \"oauth2_session_id?\",\n us.user_session_id AS \"user_session_id?\",\n us.created_at AS \"user_session_created_at?\",\n u.user_id AS \"user_id?\",\n u.username AS \"user_username?\",\n u.primary_user_email_id AS \"user_primary_user_email_id?\",\n usa.user_session_authentication_id AS \"user_session_last_authentication_id?\",\n usa.created_at AS \"user_session_last_authentication_created_at?\"\n FROM\n oauth2_authorization_grants og\n LEFT JOIN oauth2_sessions os\n USING (oauth2_session_id)\n LEFT JOIN user_sessions us\n USING (user_session_id)\n LEFT JOIN users u\n USING (user_id)\n LEFT JOIN user_session_authentications usa\n USING (user_session_id)\n\n WHERE og.authorization_code = $1\n\n ORDER BY usa.created_at DESC\n LIMIT 1\n " - }, - "1b448fe73e12bef622b75857e4c9b257c9529ca18da7f63d127e63184f4bc94b": { - "describe": { - "columns": [ - { - "name": "oauth2_authorization_grant_id", - "ordinal": 0, - "type_info": "Uuid" - }, - { - "name": "oauth2_authorization_grant_created_at", - "ordinal": 1, - "type_info": "Timestamptz" - }, - { - "name": "oauth2_authorization_grant_cancelled_at", - "ordinal": 2, - "type_info": "Timestamptz" - }, - { - "name": "oauth2_authorization_grant_fulfilled_at", - "ordinal": 3, - "type_info": "Timestamptz" - }, - { - "name": "oauth2_authorization_grant_exchanged_at", - "ordinal": 4, - "type_info": "Timestamptz" - }, - { - "name": "oauth2_authorization_grant_scope", - "ordinal": 5, - "type_info": "Text" - }, - { - "name": "oauth2_authorization_grant_state", - "ordinal": 6, - "type_info": "Text" - }, - { - "name": "oauth2_authorization_grant_redirect_uri", - "ordinal": 7, - "type_info": "Text" - }, - { - "name": "oauth2_authorization_grant_response_mode", - "ordinal": 8, - "type_info": "Text" - }, - { - "name": "oauth2_authorization_grant_nonce", - "ordinal": 9, - "type_info": "Text" - }, - { - "name": "oauth2_authorization_grant_max_age", - "ordinal": 10, - "type_info": "Int4" - }, - { - "name": "oauth2_client_id", - "ordinal": 11, - "type_info": "Uuid" - }, - { - "name": "oauth2_authorization_grant_code", - "ordinal": 12, - "type_info": "Text" - }, - { - "name": "oauth2_authorization_grant_response_type_code", - "ordinal": 13, - "type_info": "Bool" - }, - { - "name": "oauth2_authorization_grant_response_type_id_token", - "ordinal": 14, - "type_info": "Bool" - }, - { - "name": "oauth2_authorization_grant_code_challenge", - "ordinal": 15, - "type_info": "Text" - }, - { - "name": "oauth2_authorization_grant_code_challenge_method", - "ordinal": 16, - "type_info": "Text" - }, - { - "name": "oauth2_authorization_grant_requires_consent", - "ordinal": 17, - "type_info": "Bool" - }, - { - "name": "oauth2_session_id?", - "ordinal": 18, - "type_info": "Uuid" - }, - { - "name": "user_session_id?", - "ordinal": 19, - "type_info": "Uuid" - }, - { - "name": "user_session_created_at?", - "ordinal": 20, - "type_info": "Timestamptz" - }, - { - "name": "user_id?", - "ordinal": 21, - "type_info": "Uuid" - }, - { - "name": "user_username?", - "ordinal": 22, - "type_info": "Text" - }, - { - "name": "user_primary_user_email_id?", - "ordinal": 23, - "type_info": "Uuid" - }, - { - "name": "user_session_last_authentication_id?", - "ordinal": 24, - "type_info": "Uuid" - }, - { - "name": "user_session_last_authentication_created_at?", - "ordinal": 25, - "type_info": "Timestamptz" - } - ], - "nullable": [ - false, - false, - true, - true, - true, - false, - true, - false, - false, - true, - true, - false, - true, - false, - false, - true, - true, - false, - false, - false, - false, - false, - false, - true, - false, - false - ], - "parameters": { - "Left": [ - "Uuid" - ] - } - }, - "query": "\n SELECT\n og.oauth2_authorization_grant_id,\n og.created_at AS oauth2_authorization_grant_created_at,\n og.cancelled_at AS oauth2_authorization_grant_cancelled_at,\n og.fulfilled_at AS oauth2_authorization_grant_fulfilled_at,\n og.exchanged_at AS oauth2_authorization_grant_exchanged_at,\n og.scope AS oauth2_authorization_grant_scope,\n og.state AS oauth2_authorization_grant_state,\n og.redirect_uri AS oauth2_authorization_grant_redirect_uri,\n og.response_mode AS oauth2_authorization_grant_response_mode,\n og.nonce AS oauth2_authorization_grant_nonce,\n og.max_age AS oauth2_authorization_grant_max_age,\n og.oauth2_client_id AS oauth2_client_id,\n og.authorization_code AS oauth2_authorization_grant_code,\n og.response_type_code AS oauth2_authorization_grant_response_type_code,\n og.response_type_id_token AS oauth2_authorization_grant_response_type_id_token,\n og.code_challenge AS oauth2_authorization_grant_code_challenge,\n og.code_challenge_method AS oauth2_authorization_grant_code_challenge_method,\n og.requires_consent AS oauth2_authorization_grant_requires_consent,\n os.oauth2_session_id AS \"oauth2_session_id?\",\n us.user_session_id AS \"user_session_id?\",\n us.created_at AS \"user_session_created_at?\",\n u.user_id AS \"user_id?\",\n u.username AS \"user_username?\",\n u.primary_user_email_id AS \"user_primary_user_email_id?\",\n usa.user_session_authentication_id AS \"user_session_last_authentication_id?\",\n usa.created_at AS \"user_session_last_authentication_created_at?\"\n FROM\n oauth2_authorization_grants og\n LEFT JOIN oauth2_sessions os\n USING (oauth2_session_id)\n LEFT JOIN user_sessions us\n USING (user_session_id)\n LEFT JOIN users u\n USING (user_id)\n LEFT JOIN user_session_authentications usa\n USING (user_session_id)\n\n WHERE og.oauth2_authorization_grant_id = $1\n\n ORDER BY usa.created_at DESC\n LIMIT 1\n " - }, "1d372f36c382ab16264cea54537af3544ea6d6d75d10b432b07dbd0dadd2fa4e": { "describe": { "columns": [ @@ -951,6 +507,86 @@ }, "query": "\n UPDATE oauth2_authorization_grants AS og\n SET\n oauth2_session_id = os.oauth2_session_id,\n fulfilled_at = os.created_at\n FROM oauth2_sessions os\n WHERE\n og.oauth2_authorization_grant_id = $1\n AND os.oauth2_session_id = $2\n RETURNING fulfilled_at AS \"fulfilled_at!: DateTime\"\n " }, + "4f8b0cd13d9488c2dd0f183d090d3856da15dcdb57a8c113febbee665a2a3ac5": { + "describe": { + "columns": [ + { + "name": "compat_sso_login_id", + "ordinal": 0, + "type_info": "Uuid" + }, + { + "name": "compat_sso_login_token", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "compat_sso_login_redirect_uri", + "ordinal": 2, + "type_info": "Text" + }, + { + "name": "compat_sso_login_created_at", + "ordinal": 3, + "type_info": "Timestamptz" + }, + { + "name": "compat_sso_login_fulfilled_at", + "ordinal": 4, + "type_info": "Timestamptz" + }, + { + "name": "compat_sso_login_exchanged_at", + "ordinal": 5, + "type_info": "Timestamptz" + }, + { + "name": "compat_session_id?", + "ordinal": 6, + "type_info": "Uuid" + }, + { + "name": "compat_session_created_at?", + "ordinal": 7, + "type_info": "Timestamptz" + }, + { + "name": "compat_session_finished_at?", + "ordinal": 8, + "type_info": "Timestamptz" + }, + { + "name": "compat_session_device_id?", + "ordinal": 9, + "type_info": "Text" + }, + { + "name": "user_id?", + "ordinal": 10, + "type_info": "Uuid" + } + ], + "nullable": [ + false, + false, + false, + false, + true, + true, + false, + false, + true, + false, + false + ], + "parameters": { + "Left": [ + "Text" + ] + } + }, + "query": "\n SELECT cl.compat_sso_login_id\n , cl.login_token AS \"compat_sso_login_token\"\n , cl.redirect_uri AS \"compat_sso_login_redirect_uri\"\n , cl.created_at AS \"compat_sso_login_created_at\"\n , cl.fulfilled_at AS \"compat_sso_login_fulfilled_at\"\n , cl.exchanged_at AS \"compat_sso_login_exchanged_at\"\n , cs.compat_session_id AS \"compat_session_id?\"\n , cs.created_at AS \"compat_session_created_at?\"\n , cs.finished_at AS \"compat_session_finished_at?\"\n , cs.device_id AS \"compat_session_device_id?\"\n , cs.user_id AS \"user_id?\"\n FROM compat_sso_logins cl\n LEFT JOIN compat_sessions cs\n USING (compat_session_id)\n WHERE cl.login_token = $1\n " + }, "51158bfcaa1a8d8e051bffe7c5ba0369bf53fb162f7622626054e89e68fc07bd": { "describe": { "columns": [ @@ -972,104 +608,6 @@ }, "query": "\n SELECT scope_token\n FROM oauth2_consents\n WHERE user_id = $1 AND oauth2_client_id = $2\n " }, - "51bf417d259989d1228ba86fa11432e9428dece97b79e93f13921d0a510a9428": { - "describe": { - "columns": [ - { - "name": "compat_refresh_token_id", - "ordinal": 0, - "type_info": "Uuid" - }, - { - "name": "compat_refresh_token", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "compat_refresh_token_created_at", - "ordinal": 2, - "type_info": "Timestamptz" - }, - { - "name": "compat_access_token_id", - "ordinal": 3, - "type_info": "Uuid" - }, - { - "name": "compat_access_token", - "ordinal": 4, - "type_info": "Text" - }, - { - "name": "compat_access_token_created_at", - "ordinal": 5, - "type_info": "Timestamptz" - }, - { - "name": "compat_access_token_expires_at", - "ordinal": 6, - "type_info": "Timestamptz" - }, - { - "name": "compat_session_id", - "ordinal": 7, - "type_info": "Uuid" - }, - { - "name": "compat_session_created_at", - "ordinal": 8, - "type_info": "Timestamptz" - }, - { - "name": "compat_session_finished_at", - "ordinal": 9, - "type_info": "Timestamptz" - }, - { - "name": "compat_session_device_id", - "ordinal": 10, - "type_info": "Text" - }, - { - "name": "user_id", - "ordinal": 11, - "type_info": "Uuid" - }, - { - "name": "user_username!", - "ordinal": 12, - "type_info": "Text" - }, - { - "name": "user_primary_user_email_id", - "ordinal": 13, - "type_info": "Uuid" - } - ], - "nullable": [ - false, - false, - false, - false, - false, - false, - true, - false, - false, - true, - false, - false, - false, - true - ], - "parameters": { - "Left": [ - "Text" - ] - } - }, - "query": "\n SELECT\n cr.compat_refresh_token_id,\n cr.refresh_token AS \"compat_refresh_token\",\n cr.created_at AS \"compat_refresh_token_created_at\",\n ct.compat_access_token_id,\n ct.access_token AS \"compat_access_token\",\n ct.created_at AS \"compat_access_token_created_at\",\n ct.expires_at AS \"compat_access_token_expires_at\",\n cs.compat_session_id,\n cs.created_at AS \"compat_session_created_at\",\n cs.finished_at AS \"compat_session_finished_at\",\n cs.device_id AS \"compat_session_device_id\",\n u.user_id,\n u.username AS \"user_username!\",\n u.primary_user_email_id AS \"user_primary_user_email_id\"\n\n FROM compat_refresh_tokens cr\n INNER JOIN compat_sessions cs\n USING (compat_session_id)\n INNER JOIN compat_access_tokens ct\n USING (compat_access_token_id)\n INNER JOIN users u\n USING (user_id)\n\n WHERE cr.refresh_token = $1\n AND cr.consumed_at IS NULL\n AND cs.finished_at IS NULL\n " - }, "559a486756d08d101eb7188ef6637b9d24c024d056795b8121f7f04a7f9db6a3": { "describe": { "columns": [ @@ -1674,27 +1212,7 @@ }, "query": "\n UPDATE user_email_confirmation_codes\n SET consumed_at = $2\n WHERE user_email_confirmation_code_id = $1\n " }, - "94fd96446b237c87bd6bf741f3c42b37ee751b87b7fcc459602bdf8c46962443": { - "describe": { - "columns": [ - { - "name": "exists!", - "ordinal": 0, - "type_info": "Bool" - } - ], - "nullable": [ - null - ], - "parameters": { - "Left": [ - "Text" - ] - } - }, - "query": "\n SELECT EXISTS(\n SELECT 1 FROM users WHERE username = $1\n ) AS \"exists!\"\n " - }, - "976ac2435784128eab195c8e6b9bd6e8d7b3a9142c2a34538de03817a3c94e99": { + "92ef320b75ca479ed1a38f6d654fdb953431188a8654c806fd5f98444b00c012": { "describe": { "columns": [ { @@ -1751,16 +1269,6 @@ "name": "user_id?", "ordinal": 10, "type_info": "Uuid" - }, - { - "name": "user_username?", - "ordinal": 11, - "type_info": "Text" - }, - { - "name": "user_primary_user_email_id?", - "ordinal": 12, - "type_info": "Uuid" } ], "nullable": [ @@ -1774,9 +1282,7 @@ false, true, false, - false, - false, - true + false ], "parameters": { "Left": [ @@ -1784,7 +1290,27 @@ ] } }, - "query": "\n SELECT\n cl.compat_sso_login_id,\n cl.login_token AS \"compat_sso_login_token\",\n cl.redirect_uri AS \"compat_sso_login_redirect_uri\",\n cl.created_at AS \"compat_sso_login_created_at\",\n cl.fulfilled_at AS \"compat_sso_login_fulfilled_at\",\n cl.exchanged_at AS \"compat_sso_login_exchanged_at\",\n cs.compat_session_id AS \"compat_session_id?\",\n cs.created_at AS \"compat_session_created_at?\",\n cs.finished_at AS \"compat_session_finished_at?\",\n cs.device_id AS \"compat_session_device_id?\",\n u.user_id AS \"user_id?\",\n u.username AS \"user_username?\",\n u.primary_user_email_id AS \"user_primary_user_email_id?\"\n FROM compat_sso_logins cl\n LEFT JOIN compat_sessions cs\n USING (compat_session_id)\n LEFT JOIN users u\n USING (user_id)\n WHERE cl.compat_sso_login_id = $1\n " + "query": "\n SELECT cl.compat_sso_login_id\n , cl.login_token AS \"compat_sso_login_token\"\n , cl.redirect_uri AS \"compat_sso_login_redirect_uri\"\n , cl.created_at AS \"compat_sso_login_created_at\"\n , cl.fulfilled_at AS \"compat_sso_login_fulfilled_at\"\n , cl.exchanged_at AS \"compat_sso_login_exchanged_at\"\n , cs.compat_session_id AS \"compat_session_id?\"\n , cs.created_at AS \"compat_session_created_at?\"\n , cs.finished_at AS \"compat_session_finished_at?\"\n , cs.device_id AS \"compat_session_device_id?\"\n , cs.user_id AS \"user_id?\"\n\n FROM compat_sso_logins cl\n LEFT JOIN compat_sessions cs\n USING (compat_session_id)\n WHERE cl.compat_sso_login_id = $1\n " + }, + "94fd96446b237c87bd6bf741f3c42b37ee751b87b7fcc459602bdf8c46962443": { + "describe": { + "columns": [ + { + "name": "exists!", + "ordinal": 0, + "type_info": "Bool" + } + ], + "nullable": [ + null + ], + "parameters": { + "Left": [ + "Text" + ] + } + }, + "query": "\n SELECT EXISTS(\n SELECT 1 FROM users WHERE username = $1\n ) AS \"exists!\"\n " }, "99f5f9eb0adc5ec120ed8194cbf6a8545155bef09e6d94d92fb67fd1b14d4f28": { "describe": { @@ -2017,6 +1543,140 @@ }, "query": "\n INSERT INTO user_sessions (user_session_id, user_id, created_at)\n VALUES ($1, $2, $3)\n " }, + "c467144ae98322e3ed6d34df6626d63c15bdfc7137e12097cfb6f9398f7029ca": { + "describe": { + "columns": [ + { + "name": "oauth2_authorization_grant_id", + "ordinal": 0, + "type_info": "Uuid" + }, + { + "name": "oauth2_authorization_grant_created_at", + "ordinal": 1, + "type_info": "Timestamptz" + }, + { + "name": "oauth2_authorization_grant_cancelled_at", + "ordinal": 2, + "type_info": "Timestamptz" + }, + { + "name": "oauth2_authorization_grant_fulfilled_at", + "ordinal": 3, + "type_info": "Timestamptz" + }, + { + "name": "oauth2_authorization_grant_exchanged_at", + "ordinal": 4, + "type_info": "Timestamptz" + }, + { + "name": "oauth2_authorization_grant_scope", + "ordinal": 5, + "type_info": "Text" + }, + { + "name": "oauth2_authorization_grant_state", + "ordinal": 6, + "type_info": "Text" + }, + { + "name": "oauth2_authorization_grant_redirect_uri", + "ordinal": 7, + "type_info": "Text" + }, + { + "name": "oauth2_authorization_grant_response_mode", + "ordinal": 8, + "type_info": "Text" + }, + { + "name": "oauth2_authorization_grant_nonce", + "ordinal": 9, + "type_info": "Text" + }, + { + "name": "oauth2_authorization_grant_max_age", + "ordinal": 10, + "type_info": "Int4" + }, + { + "name": "oauth2_client_id", + "ordinal": 11, + "type_info": "Uuid" + }, + { + "name": "oauth2_authorization_grant_code", + "ordinal": 12, + "type_info": "Text" + }, + { + "name": "oauth2_authorization_grant_response_type_code", + "ordinal": 13, + "type_info": "Bool" + }, + { + "name": "oauth2_authorization_grant_response_type_id_token", + "ordinal": 14, + "type_info": "Bool" + }, + { + "name": "oauth2_authorization_grant_code_challenge", + "ordinal": 15, + "type_info": "Text" + }, + { + "name": "oauth2_authorization_grant_code_challenge_method", + "ordinal": 16, + "type_info": "Text" + }, + { + "name": "oauth2_authorization_grant_requires_consent", + "ordinal": 17, + "type_info": "Bool" + }, + { + "name": "oauth2_session_id?", + "ordinal": 18, + "type_info": "Uuid" + }, + { + "name": "user_session_id?", + "ordinal": 19, + "type_info": "Uuid" + } + ], + "nullable": [ + false, + false, + true, + true, + true, + false, + true, + false, + false, + true, + true, + false, + true, + false, + false, + true, + true, + false, + false, + false + ], + "parameters": { + "Left": [ + "Text" + ] + } + }, + "query": "\n SELECT og.oauth2_authorization_grant_id\n , og.created_at AS oauth2_authorization_grant_created_at\n , og.cancelled_at AS oauth2_authorization_grant_cancelled_at\n , og.fulfilled_at AS oauth2_authorization_grant_fulfilled_at\n , og.exchanged_at AS oauth2_authorization_grant_exchanged_at\n , og.scope AS oauth2_authorization_grant_scope\n , og.state AS oauth2_authorization_grant_state\n , og.redirect_uri AS oauth2_authorization_grant_redirect_uri\n , og.response_mode AS oauth2_authorization_grant_response_mode\n , og.nonce AS oauth2_authorization_grant_nonce\n , og.max_age AS oauth2_authorization_grant_max_age\n , og.oauth2_client_id AS oauth2_client_id\n , og.authorization_code AS oauth2_authorization_grant_code\n , og.response_type_code AS oauth2_authorization_grant_response_type_code\n , og.response_type_id_token AS oauth2_authorization_grant_response_type_id_token\n , og.code_challenge AS oauth2_authorization_grant_code_challenge\n , og.code_challenge_method AS oauth2_authorization_grant_code_challenge_method\n , og.requires_consent AS oauth2_authorization_grant_requires_consent\n , os.oauth2_session_id AS \"oauth2_session_id?\"\n , os.user_session_id AS \"user_session_id?\"\n FROM\n oauth2_authorization_grants og\n LEFT JOIN oauth2_sessions os\n USING (oauth2_session_id)\n\n WHERE og.authorization_code = $1\n " + }, "c88376abdba124ff0487a9a69d2345c7d69d7394f355111ec369cfa6d45fb40f": { "describe": { "columns": [], @@ -2043,22 +1703,7 @@ }, "query": "\n INSERT INTO oauth2_authorization_grants (\n oauth2_authorization_grant_id,\n oauth2_client_id,\n redirect_uri,\n scope,\n state,\n nonce,\n max_age,\n response_mode,\n code_challenge,\n code_challenge_method,\n response_type_code,\n response_type_id_token,\n authorization_code,\n requires_consent,\n created_at\n )\n VALUES\n ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)\n " }, - "caf54e4659306a746747aa61906bdb2cb8da51176e90435aa8b9754ebf3e4d60": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Uuid", - "Uuid", - "Text", - "Timestamptz" - ] - } - }, - "query": "\n INSERT INTO compat_sessions (compat_session_id, user_id, device_id, created_at)\n VALUES ($1, $2, $3, $4)\n " - }, - "d023d7346ec1f32da9459db3c39dffd8a4e3d4e91cdf096928de4517d3f8c622": { + "cad4d47709278a9ddbebfc91642967b465bafa596827d9b86a336841b2cfbf0c": { "describe": { "columns": [ { @@ -2115,36 +1760,6 @@ "name": "user_session_id!", "ordinal": 10, "type_info": "Uuid" - }, - { - "name": "user_session_created_at!", - "ordinal": 11, - "type_info": "Timestamptz" - }, - { - "name": "user_id!", - "ordinal": 12, - "type_info": "Uuid" - }, - { - "name": "user_username!", - "ordinal": 13, - "type_info": "Text" - }, - { - "name": "user_primary_user_email_id", - "ordinal": 14, - "type_info": "Uuid" - }, - { - "name": "user_session_last_authentication_id?", - "ordinal": 15, - "type_info": "Uuid" - }, - { - "name": "user_session_last_authentication_created_at?", - "ordinal": 16, - "type_info": "Timestamptz" } ], "nullable": [ @@ -2158,12 +1773,6 @@ false, false, false, - false, - false, - false, - false, - true, - false, false ], "parameters": { @@ -2172,7 +1781,156 @@ ] } }, - "query": "\n SELECT\n rt.oauth2_refresh_token_id,\n rt.refresh_token AS oauth2_refresh_token,\n rt.created_at AS oauth2_refresh_token_created_at,\n at.oauth2_access_token_id AS \"oauth2_access_token_id?\",\n at.access_token AS \"oauth2_access_token?\",\n at.created_at AS \"oauth2_access_token_created_at?\",\n at.expires_at AS \"oauth2_access_token_expires_at?\",\n os.oauth2_session_id AS \"oauth2_session_id!\",\n os.oauth2_client_id AS \"oauth2_client_id!\",\n os.scope AS \"oauth2_session_scope!\",\n us.user_session_id AS \"user_session_id!\",\n us.created_at AS \"user_session_created_at!\",\n u.user_id AS \"user_id!\",\n u.username AS \"user_username!\",\n u.primary_user_email_id AS \"user_primary_user_email_id\",\n usa.user_session_authentication_id AS \"user_session_last_authentication_id?\",\n usa.created_at AS \"user_session_last_authentication_created_at?\"\n FROM oauth2_refresh_tokens rt\n INNER JOIN oauth2_sessions os\n USING (oauth2_session_id)\n LEFT JOIN oauth2_access_tokens at\n USING (oauth2_access_token_id)\n INNER JOIN user_sessions us\n USING (user_session_id)\n INNER JOIN users u\n USING (user_id)\n LEFT JOIN user_session_authentications usa\n USING (user_session_id)\n LEFT JOIN user_emails ue\n ON ue.user_email_id = u.primary_user_email_id\n\n WHERE rt.refresh_token = $1\n AND rt.consumed_at IS NULL\n AND rt.revoked_at IS NULL\n AND us.finished_at IS NULL\n AND os.finished_at IS NULL\n\n ORDER BY usa.created_at DESC\n LIMIT 1\n " + "query": "\n SELECT rt.oauth2_refresh_token_id\n , rt.refresh_token AS oauth2_refresh_token\n , rt.created_at AS oauth2_refresh_token_created_at\n , at.oauth2_access_token_id AS \"oauth2_access_token_id?\"\n , at.access_token AS \"oauth2_access_token?\"\n , at.created_at AS \"oauth2_access_token_created_at?\"\n , at.expires_at AS \"oauth2_access_token_expires_at?\"\n , os.oauth2_session_id AS \"oauth2_session_id!\"\n , os.oauth2_client_id AS \"oauth2_client_id!\"\n , os.scope AS \"oauth2_session_scope!\"\n , os.user_session_id AS \"user_session_id!\"\n FROM oauth2_refresh_tokens rt\n INNER JOIN oauth2_sessions os\n USING (oauth2_session_id)\n LEFT JOIN oauth2_access_tokens at\n USING (oauth2_access_token_id)\n\n WHERE rt.refresh_token = $1\n AND rt.consumed_at IS NULL\n AND rt.revoked_at IS NULL\n AND os.finished_at IS NULL\n " + }, + "caf54e4659306a746747aa61906bdb2cb8da51176e90435aa8b9754ebf3e4d60": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Uuid", + "Uuid", + "Text", + "Timestamptz" + ] + } + }, + "query": "\n INSERT INTO compat_sessions (compat_session_id, user_id, device_id, created_at)\n VALUES ($1, $2, $3, $4)\n " + }, + "d08b787fc422b6699ffc0a491ecf92fb993db0aca51534b315bcfa4891baca84": { + "describe": { + "columns": [ + { + "name": "oauth2_authorization_grant_id", + "ordinal": 0, + "type_info": "Uuid" + }, + { + "name": "oauth2_authorization_grant_created_at", + "ordinal": 1, + "type_info": "Timestamptz" + }, + { + "name": "oauth2_authorization_grant_cancelled_at", + "ordinal": 2, + "type_info": "Timestamptz" + }, + { + "name": "oauth2_authorization_grant_fulfilled_at", + "ordinal": 3, + "type_info": "Timestamptz" + }, + { + "name": "oauth2_authorization_grant_exchanged_at", + "ordinal": 4, + "type_info": "Timestamptz" + }, + { + "name": "oauth2_authorization_grant_scope", + "ordinal": 5, + "type_info": "Text" + }, + { + "name": "oauth2_authorization_grant_state", + "ordinal": 6, + "type_info": "Text" + }, + { + "name": "oauth2_authorization_grant_redirect_uri", + "ordinal": 7, + "type_info": "Text" + }, + { + "name": "oauth2_authorization_grant_response_mode", + "ordinal": 8, + "type_info": "Text" + }, + { + "name": "oauth2_authorization_grant_nonce", + "ordinal": 9, + "type_info": "Text" + }, + { + "name": "oauth2_authorization_grant_max_age", + "ordinal": 10, + "type_info": "Int4" + }, + { + "name": "oauth2_client_id", + "ordinal": 11, + "type_info": "Uuid" + }, + { + "name": "oauth2_authorization_grant_code", + "ordinal": 12, + "type_info": "Text" + }, + { + "name": "oauth2_authorization_grant_response_type_code", + "ordinal": 13, + "type_info": "Bool" + }, + { + "name": "oauth2_authorization_grant_response_type_id_token", + "ordinal": 14, + "type_info": "Bool" + }, + { + "name": "oauth2_authorization_grant_code_challenge", + "ordinal": 15, + "type_info": "Text" + }, + { + "name": "oauth2_authorization_grant_code_challenge_method", + "ordinal": 16, + "type_info": "Text" + }, + { + "name": "oauth2_authorization_grant_requires_consent", + "ordinal": 17, + "type_info": "Bool" + }, + { + "name": "oauth2_session_id?", + "ordinal": 18, + "type_info": "Uuid" + }, + { + "name": "user_session_id?", + "ordinal": 19, + "type_info": "Uuid" + } + ], + "nullable": [ + false, + false, + true, + true, + true, + false, + true, + false, + false, + true, + true, + false, + true, + false, + false, + true, + true, + false, + false, + false + ], + "parameters": { + "Left": [ + "Uuid" + ] + } + }, + "query": "\n SELECT og.oauth2_authorization_grant_id\n , og.created_at AS oauth2_authorization_grant_created_at\n , og.cancelled_at AS oauth2_authorization_grant_cancelled_at\n , og.fulfilled_at AS oauth2_authorization_grant_fulfilled_at\n , og.exchanged_at AS oauth2_authorization_grant_exchanged_at\n , og.scope AS oauth2_authorization_grant_scope\n , og.state AS oauth2_authorization_grant_state\n , og.redirect_uri AS oauth2_authorization_grant_redirect_uri\n , og.response_mode AS oauth2_authorization_grant_response_mode\n , og.nonce AS oauth2_authorization_grant_nonce\n , og.max_age AS oauth2_authorization_grant_max_age\n , og.oauth2_client_id AS oauth2_client_id\n , og.authorization_code AS oauth2_authorization_grant_code\n , og.response_type_code AS oauth2_authorization_grant_response_type_code\n , og.response_type_id_token AS oauth2_authorization_grant_response_type_id_token\n , og.code_challenge AS oauth2_authorization_grant_code_challenge\n , og.code_challenge_method AS oauth2_authorization_grant_code_challenge_method\n , og.requires_consent AS oauth2_authorization_grant_requires_consent\n , os.oauth2_session_id AS \"oauth2_session_id?\"\n , os.user_session_id AS \"user_session_id?\"\n FROM\n oauth2_authorization_grants og\n LEFT JOIN oauth2_sessions os\n USING (oauth2_session_id)\n\n WHERE og.oauth2_authorization_grant_id = $1\n " }, "d12a513b81b3ef658eae1f0a719933323f28c6ee260b52cafe337dd3d19e865c": { "describe": { @@ -2209,6 +1967,75 @@ }, "query": "\n INSERT INTO compat_sso_logins\n (compat_sso_login_id, login_token, redirect_uri, created_at)\n VALUES ($1, $2, $3, $4)\n " }, + "d4c6c070a0cd889cef9e0cfd65c64522a03f0bae12ee7c6b74343ec8f38d24c1": { + "describe": { + "columns": [ + { + "name": "compat_access_token_id", + "ordinal": 0, + "type_info": "Uuid" + }, + { + "name": "compat_access_token", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "compat_access_token_created_at", + "ordinal": 2, + "type_info": "Timestamptz" + }, + { + "name": "compat_access_token_expires_at", + "ordinal": 3, + "type_info": "Timestamptz" + }, + { + "name": "compat_session_id", + "ordinal": 4, + "type_info": "Uuid" + }, + { + "name": "compat_session_created_at", + "ordinal": 5, + "type_info": "Timestamptz" + }, + { + "name": "compat_session_finished_at", + "ordinal": 6, + "type_info": "Timestamptz" + }, + { + "name": "compat_session_device_id", + "ordinal": 7, + "type_info": "Text" + }, + { + "name": "user_id!", + "ordinal": 8, + "type_info": "Uuid" + } + ], + "nullable": [ + false, + false, + false, + true, + false, + false, + true, + false, + false + ], + "parameters": { + "Left": [ + "Text", + "Timestamptz" + ] + } + }, + "query": "\n SELECT ct.compat_access_token_id\n , ct.access_token AS \"compat_access_token\"\n , ct.created_at AS \"compat_access_token_created_at\"\n , ct.expires_at AS \"compat_access_token_expires_at\"\n , cs.compat_session_id\n , cs.created_at AS \"compat_session_created_at\"\n , cs.finished_at AS \"compat_session_finished_at\"\n , cs.device_id AS \"compat_session_device_id\"\n , cs.user_id AS \"user_id!\"\n\n FROM compat_access_tokens ct\n INNER JOIN compat_sessions cs\n USING (compat_session_id)\n\n WHERE ct.access_token = $1\n AND (ct.expires_at < $2 OR ct.expires_at IS NULL)\n AND cs.finished_at IS NULL \n " + }, "d8677b3b6ee594c230fad98c1aa1c6e3d983375bf5b701c7b52468e7f906abf9": { "describe": { "columns": [], @@ -2426,62 +2253,47 @@ }, "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 )\n VALUES\n ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)\n " }, - "f624e1bdbff4e97b300362d1bbd86035e4a0fdd8ffe16c3bfb9bc451ba60851b": { + "fba88894ee24cd181f50412571a19ee658f77012d330e7dab43a3c18d549355a": { "describe": { "columns": [ { - "name": "compat_access_token_id", + "name": "oauth2_access_token_id", "ordinal": 0, "type_info": "Uuid" }, { - "name": "compat_access_token", + "name": "oauth2_access_token", "ordinal": 1, "type_info": "Text" }, { - "name": "compat_access_token_created_at", + "name": "oauth2_access_token_created_at", "ordinal": 2, "type_info": "Timestamptz" }, { - "name": "compat_access_token_expires_at", + "name": "oauth2_access_token_expires_at", "ordinal": 3, "type_info": "Timestamptz" }, { - "name": "compat_session_id", + "name": "oauth2_session_id!", "ordinal": 4, "type_info": "Uuid" }, { - "name": "compat_session_created_at", + "name": "oauth2_client_id!", "ordinal": 5, - "type_info": "Timestamptz" - }, - { - "name": "compat_session_finished_at", - "ordinal": 6, - "type_info": "Timestamptz" - }, - { - "name": "compat_session_device_id", - "ordinal": 7, - "type_info": "Text" - }, - { - "name": "user_id!", - "ordinal": 8, "type_info": "Uuid" }, { - "name": "user_username!", - "ordinal": 9, + "name": "scope!", + "ordinal": 6, "type_info": "Text" }, { - "name": "user_primary_user_email_id", - "ordinal": 10, + "name": "user_session_id!", + "ordinal": 7, "type_info": "Uuid" } ], @@ -2489,22 +2301,18 @@ false, false, false, - true, - false, - false, - true, false, false, false, - true + false, + false ], "parameters": { "Left": [ - "Text", - "Timestamptz" + "Text" ] } }, - "query": "\n SELECT\n ct.compat_access_token_id,\n ct.access_token AS \"compat_access_token\",\n ct.created_at AS \"compat_access_token_created_at\",\n ct.expires_at AS \"compat_access_token_expires_at\",\n cs.compat_session_id,\n cs.created_at AS \"compat_session_created_at\",\n cs.finished_at AS \"compat_session_finished_at\",\n cs.device_id AS \"compat_session_device_id\",\n u.user_id AS \"user_id!\",\n u.username AS \"user_username!\",\n u.primary_user_email_id AS \"user_primary_user_email_id\"\n\n FROM compat_access_tokens ct\n INNER JOIN compat_sessions cs\n USING (compat_session_id)\n INNER JOIN users u\n USING (user_id)\n\n WHERE ct.access_token = $1\n AND (ct.expires_at < $2 OR ct.expires_at IS NULL)\n AND cs.finished_at IS NULL \n " + "query": "\n SELECT at.oauth2_access_token_id\n , at.access_token AS \"oauth2_access_token\"\n , at.created_at AS \"oauth2_access_token_created_at\"\n , at.expires_at AS \"oauth2_access_token_expires_at\"\n , os.oauth2_session_id AS \"oauth2_session_id!\"\n , os.oauth2_client_id AS \"oauth2_client_id!\"\n , os.scope AS \"scope!\"\n , os.user_session_id AS \"user_session_id!\"\n\n FROM oauth2_access_tokens at\n INNER JOIN oauth2_sessions os\n USING (oauth2_session_id)\n\n WHERE at.access_token = $1\n AND at.revoked_at IS NULL\n AND os.finished_at IS NULL\n " } } \ No newline at end of file diff --git a/crates/storage/src/compat.rs b/crates/storage/src/compat.rs index 4708e91b..ba47990d 100644 --- a/crates/storage/src/compat.rs +++ b/crates/storage/src/compat.rs @@ -39,8 +39,6 @@ struct CompatAccessTokenLookup { compat_session_finished_at: Option>, compat_session_device_id: String, user_id: Uuid, - user_username: String, - user_primary_user_email_id: Option, } #[tracing::instrument(skip_all, err)] @@ -52,24 +50,19 @@ pub async fn lookup_active_compat_access_token( let res = sqlx::query_as!( CompatAccessTokenLookup, r#" - SELECT - ct.compat_access_token_id, - ct.access_token AS "compat_access_token", - ct.created_at AS "compat_access_token_created_at", - ct.expires_at AS "compat_access_token_expires_at", - cs.compat_session_id, - cs.created_at AS "compat_session_created_at", - cs.finished_at AS "compat_session_finished_at", - cs.device_id AS "compat_session_device_id", - u.user_id AS "user_id!", - u.username AS "user_username!", - u.primary_user_email_id AS "user_primary_user_email_id" + SELECT ct.compat_access_token_id + , ct.access_token AS "compat_access_token" + , ct.created_at AS "compat_access_token_created_at" + , ct.expires_at AS "compat_access_token_expires_at" + , cs.compat_session_id + , cs.created_at AS "compat_session_created_at" + , cs.finished_at AS "compat_session_finished_at" + , cs.device_id AS "compat_session_device_id" + , cs.user_id AS "user_id!" FROM compat_access_tokens ct INNER JOIN compat_sessions cs USING (compat_session_id) - INNER JOIN users u - USING (user_id) WHERE ct.access_token = $1 AND (ct.expires_at < $2 OR ct.expires_at IS NULL) @@ -92,14 +85,6 @@ pub async fn lookup_active_compat_access_token( expires_at: res.compat_access_token_expires_at, }; - let user_id = Ulid::from(res.user_id); - let user = User { - id: user_id, - username: res.user_username, - sub: user_id.to_string(), - primary_user_email_id: res.user_primary_user_email_id.map(Into::into), - }; - let id = res.compat_session_id.into(); let device = Device::try_from(res.compat_session_device_id).map_err(|e| { DatabaseInconsistencyError::on("compat_sessions") @@ -110,7 +95,7 @@ pub async fn lookup_active_compat_access_token( let session = CompatSession { id, - user, + user_id: res.user_id.into(), device, created_at: res.compat_session_created_at, finished_at: res.compat_session_finished_at, @@ -132,8 +117,6 @@ pub struct CompatRefreshTokenLookup { compat_session_finished_at: Option>, compat_session_device_id: String, user_id: Uuid, - user_username: String, - user_primary_user_email_id: Option, } #[tracing::instrument(skip_all, err)] @@ -145,29 +128,24 @@ pub async fn lookup_active_compat_refresh_token( let res = sqlx::query_as!( CompatRefreshTokenLookup, r#" - SELECT - cr.compat_refresh_token_id, - cr.refresh_token AS "compat_refresh_token", - cr.created_at AS "compat_refresh_token_created_at", - ct.compat_access_token_id, - ct.access_token AS "compat_access_token", - ct.created_at AS "compat_access_token_created_at", - ct.expires_at AS "compat_access_token_expires_at", - cs.compat_session_id, - cs.created_at AS "compat_session_created_at", - cs.finished_at AS "compat_session_finished_at", - cs.device_id AS "compat_session_device_id", - u.user_id, - u.username AS "user_username!", - u.primary_user_email_id AS "user_primary_user_email_id" + SELECT cr.compat_refresh_token_id + , cr.refresh_token AS "compat_refresh_token" + , cr.created_at AS "compat_refresh_token_created_at" + , ct.compat_access_token_id + , ct.access_token AS "compat_access_token" + , ct.created_at AS "compat_access_token_created_at" + , ct.expires_at AS "compat_access_token_expires_at" + , cs.compat_session_id + , cs.created_at AS "compat_session_created_at" + , cs.finished_at AS "compat_session_finished_at" + , cs.device_id AS "compat_session_device_id" + , cs.user_id FROM compat_refresh_tokens cr INNER JOIN compat_sessions cs USING (compat_session_id) INNER JOIN compat_access_tokens ct USING (compat_access_token_id) - INNER JOIN users u - USING (user_id) WHERE cr.refresh_token = $1 AND cr.consumed_at IS NULL @@ -195,25 +173,17 @@ pub async fn lookup_active_compat_refresh_token( expires_at: res.compat_access_token_expires_at, }; - let user_id = Ulid::from(res.user_id); - let user = User { - id: user_id, - username: res.user_username, - sub: user_id.to_string(), - primary_user_email_id: res.user_primary_user_email_id.map(Into::into), - }; - - let session_id = res.compat_session_id.into(); + let id = res.compat_session_id.into(); let device = Device::try_from(res.compat_session_device_id).map_err(|e| { DatabaseInconsistencyError::on("compat_sessions") .column("device_id") - .row(session_id) + .row(id) .source(e) })?; let session = CompatSession { - id: session_id, - user, + id, + user_id: res.user_id.into(), device, created_at: res.compat_session_created_at, finished_at: res.compat_session_finished_at, @@ -228,7 +198,7 @@ pub async fn lookup_active_compat_refresh_token( compat_session.id = %session.id, compat_session.device.id = session.device.as_str(), compat_access_token.id, - user.id = %session.user.id, + user.id = %session.user_id, ), err, )] @@ -305,7 +275,7 @@ pub async fn expire_compat_access_token( compat_session.device.id = session.device.as_str(), compat_access_token.id = %access_token.id, compat_refresh_token.id, - user.id = %session.user.id, + user.id = %session.user_id, ), err, )] @@ -469,8 +439,6 @@ struct CompatSsoLoginLookup { compat_session_finished_at: Option>, compat_session_device_id: Option, user_id: Option, - user_username: Option, - user_primary_user_email_id: Option, } impl TryFrom for CompatSsoLogin { @@ -485,33 +453,14 @@ impl TryFrom for CompatSsoLogin { .source(e) })?; - let user = match ( - res.user_id, - res.user_username, - res.user_primary_user_email_id, - ) { - (Some(id), Some(username), primary_email_id) => { - let id = Ulid::from(id); - Some(User { - id, - username, - sub: id.to_string(), - primary_user_email_id: primary_email_id.map(Into::into), - }) - } - - (None, None, None) => None, - _ => return Err(DatabaseInconsistencyError::on("compat_sessions").column("user_id")), - }; - let session = match ( res.compat_session_id, res.compat_session_device_id, res.compat_session_created_at, res.compat_session_finished_at, - user, + res.user_id, ) { - (Some(id), Some(device_id), Some(created_at), finished_at, Some(user)) => { + (Some(id), Some(device_id), Some(created_at), finished_at, Some(user_id)) => { let id = id.into(); let device = Device::try_from(device_id).map_err(|e| { DatabaseInconsistencyError::on("compat_sessions") @@ -521,7 +470,7 @@ impl TryFrom for CompatSsoLogin { })?; Some(CompatSession { id, - user, + user_id: user_id.into(), device, created_at, finished_at, @@ -579,25 +528,21 @@ pub async fn get_compat_sso_login_by_id( let res = sqlx::query_as!( CompatSsoLoginLookup, r#" - SELECT - cl.compat_sso_login_id, - cl.login_token AS "compat_sso_login_token", - cl.redirect_uri AS "compat_sso_login_redirect_uri", - cl.created_at AS "compat_sso_login_created_at", - cl.fulfilled_at AS "compat_sso_login_fulfilled_at", - cl.exchanged_at AS "compat_sso_login_exchanged_at", - cs.compat_session_id AS "compat_session_id?", - cs.created_at AS "compat_session_created_at?", - cs.finished_at AS "compat_session_finished_at?", - cs.device_id AS "compat_session_device_id?", - u.user_id AS "user_id?", - u.username AS "user_username?", - u.primary_user_email_id AS "user_primary_user_email_id?" + SELECT cl.compat_sso_login_id + , cl.login_token AS "compat_sso_login_token" + , cl.redirect_uri AS "compat_sso_login_redirect_uri" + , cl.created_at AS "compat_sso_login_created_at" + , cl.fulfilled_at AS "compat_sso_login_fulfilled_at" + , cl.exchanged_at AS "compat_sso_login_exchanged_at" + , cs.compat_session_id AS "compat_session_id?" + , cs.created_at AS "compat_session_created_at?" + , cs.finished_at AS "compat_session_finished_at?" + , cs.device_id AS "compat_session_device_id?" + , cs.user_id AS "user_id?" + FROM compat_sso_logins cl LEFT JOIN compat_sessions cs USING (compat_session_id) - LEFT JOIN users u - USING (user_id) WHERE cl.compat_sso_login_id = $1 "#, Uuid::from(id), @@ -632,25 +577,20 @@ pub async fn get_paginated_user_compat_sso_logins( // because we already have them let mut query = QueryBuilder::new( r#" - SELECT - cl.compat_sso_login_id, - cl.login_token AS "compat_sso_login_token", - cl.redirect_uri AS "compat_sso_login_redirect_uri", - cl.created_at AS "compat_sso_login_created_at", - cl.fulfilled_at AS "compat_sso_login_fulfilled_at", - cl.exchanged_at AS "compat_sso_login_exchanged_at", - cs.compat_session_id AS "compat_session_id", - cs.created_at AS "compat_session_created_at", - cs.finished_at AS "compat_session_finished_at", - cs.device_id AS "compat_session_device_id", - u.user_id AS "user_id", - u.username AS "user_username", - u.primary_user_email_id AS "user_primary_user_email_id?" + SELECT cl.compat_sso_login_id + , cl.login_token AS "compat_sso_login_token" + , cl.redirect_uri AS "compat_sso_login_redirect_uri" + , cl.created_at AS "compat_sso_login_created_at" + , cl.fulfilled_at AS "compat_sso_login_fulfilled_at" + , cl.exchanged_at AS "compat_sso_login_exchanged_at" + , cs.compat_session_id AS "compat_session_id" + , cs.created_at AS "compat_session_created_at" + , cs.finished_at AS "compat_session_finished_at" + , cs.device_id AS "compat_session_device_id" + , cs.user_id FROM compat_sso_logins cl LEFT JOIN compat_sessions cs USING (compat_session_id) - LEFT JOIN users u - USING (user_id) "#, ); @@ -683,25 +623,20 @@ pub async fn get_compat_sso_login_by_token( let res = sqlx::query_as!( CompatSsoLoginLookup, r#" - SELECT - cl.compat_sso_login_id, - cl.login_token AS "compat_sso_login_token", - cl.redirect_uri AS "compat_sso_login_redirect_uri", - cl.created_at AS "compat_sso_login_created_at", - cl.fulfilled_at AS "compat_sso_login_fulfilled_at", - cl.exchanged_at AS "compat_sso_login_exchanged_at", - cs.compat_session_id AS "compat_session_id?", - cs.created_at AS "compat_session_created_at?", - cs.finished_at AS "compat_session_finished_at?", - cs.device_id AS "compat_session_device_id?", - u.user_id AS "user_id?", - u.username AS "user_username?", - u.primary_user_email_id AS "user_primary_user_email_id?" + SELECT cl.compat_sso_login_id + , cl.login_token AS "compat_sso_login_token" + , cl.redirect_uri AS "compat_sso_login_redirect_uri" + , cl.created_at AS "compat_sso_login_created_at" + , cl.fulfilled_at AS "compat_sso_login_fulfilled_at" + , cl.exchanged_at AS "compat_sso_login_exchanged_at" + , cs.compat_session_id AS "compat_session_id?" + , cs.created_at AS "compat_session_created_at?" + , cs.finished_at AS "compat_session_finished_at?" + , cs.device_id AS "compat_session_device_id?" + , cs.user_id AS "user_id?" FROM compat_sso_logins cl LEFT JOIN compat_sessions cs USING (compat_session_id) - LEFT JOIN users u - USING (user_id) WHERE cl.login_token = $1 "#, token, @@ -729,7 +664,7 @@ pub async fn start_compat_session( executor: impl PgExecutor<'_>, mut rng: impl Rng + Send, clock: &Clock, - user: User, + user: &User, device: Device, ) -> Result { let created_at = clock.now(); @@ -751,7 +686,7 @@ pub async fn start_compat_session( Ok(CompatSession { id, - user, + user_id: user.id, device, created_at, finished_at: None, @@ -773,7 +708,7 @@ pub async fn fullfill_compat_sso_login( conn: impl Acquire<'_, Database = Postgres> + Send, mut rng: impl Rng + Send, clock: &Clock, - user: User, + user: &User, mut compat_sso_login: CompatSsoLogin, device: Device, ) -> Result { diff --git a/crates/storage/src/oauth2/access_token.rs b/crates/storage/src/oauth2/access_token.rs index e41f4812..c85d1b48 100644 --- a/crates/storage/src/oauth2/access_token.rs +++ b/crates/storage/src/oauth2/access_token.rs @@ -13,21 +13,20 @@ // limitations under the License. use chrono::{DateTime, Duration, Utc}; -use mas_data_model::{AccessToken, Authentication, BrowserSession, Session, User}; +use mas_data_model::{AccessToken, Session}; use rand::Rng; use sqlx::{PgConnection, PgExecutor}; use ulid::Ulid; use uuid::Uuid; -use super::client::OAuth2ClientRepository; -use crate::{Clock, DatabaseError, DatabaseInconsistencyError, Repository}; +use crate::{Clock, DatabaseError, DatabaseInconsistencyError}; #[tracing::instrument( skip_all, fields( %session.id, - client.id = %session.client.id, - user.id = %session.browser_session.user.id, + user_session.id = %session.user_session_id, + client.id = %session.client_id, access_token.id, ), err, @@ -81,12 +80,6 @@ pub struct OAuth2AccessTokenLookup { oauth2_client_id: Uuid, scope: String, user_session_id: Uuid, - user_session_created_at: DateTime, - user_id: Uuid, - user_username: String, - user_primary_user_email_id: Option, - user_session_last_authentication_id: Option, - user_session_last_authentication_created_at: Option>, } #[allow(clippy::too_many_lines)] @@ -104,30 +97,15 @@ pub async fn lookup_active_access_token( , os.oauth2_session_id AS "oauth2_session_id!" , os.oauth2_client_id AS "oauth2_client_id!" , os.scope AS "scope!" - , us.user_session_id AS "user_session_id!" - , us.created_at AS "user_session_created_at!" - , u.user_id AS "user_id!" - , u.username AS "user_username!" - , u.primary_user_email_id AS "user_primary_user_email_id" - , usa.user_session_authentication_id AS "user_session_last_authentication_id?" - , usa.created_at AS "user_session_last_authentication_created_at?" + , os.user_session_id AS "user_session_id!" FROM oauth2_access_tokens at INNER JOIN oauth2_sessions os USING (oauth2_session_id) - INNER JOIN user_sessions us - USING (user_session_id) - INNER JOIN users u - USING (user_id) - LEFT JOIN user_session_authentications usa - USING (user_session_id) WHERE at.access_token = $1 AND at.revoked_at IS NULL AND os.finished_at IS NULL - - ORDER BY usa.created_at DESC - LIMIT 1 "#, token, ) @@ -144,44 +122,6 @@ pub async fn lookup_active_access_token( }; let session_id = res.oauth2_session_id.into(); - let client = conn - .oauth2_client() - .lookup(res.oauth2_client_id.into()) - .await? - .ok_or_else(|| { - DatabaseInconsistencyError::on("oauth2_sessions") - .column("client_id") - .row(session_id) - })?; - - let user_id = Ulid::from(res.user_id); - let user = User { - id: user_id, - username: res.user_username, - sub: user_id.to_string(), - primary_user_email_id: res.user_primary_user_email_id.map(Into::into), - }; - - let last_authentication = match ( - res.user_session_last_authentication_id, - res.user_session_last_authentication_created_at, - ) { - (None, None) => None, - (Some(id), Some(created_at)) => Some(Authentication { - id: id.into(), - created_at, - }), - _ => return Err(DatabaseInconsistencyError::on("user_session_authentications").into()), - }; - - let browser_session = BrowserSession { - id: res.user_session_id.into(), - created_at: res.user_session_created_at, - finished_at: None, - user, - last_authentication, - }; - let scope = res.scope.parse().map_err(|e| { DatabaseInconsistencyError::on("oauth2_sessions") .column("scope") @@ -191,8 +131,8 @@ pub async fn lookup_active_access_token( let session = Session { id: session_id, - client, - browser_session, + client_id: res.oauth2_client_id.into(), + user_session_id: res.user_session_id.into(), scope, }; diff --git a/crates/storage/src/oauth2/authorization_grant.rs b/crates/storage/src/oauth2/authorization_grant.rs index bfd91860..3a18ef41 100644 --- a/crates/storage/src/oauth2/authorization_grant.rs +++ b/crates/storage/src/oauth2/authorization_grant.rs @@ -16,8 +16,8 @@ use std::num::NonZeroU32; use chrono::{DateTime, Utc}; use mas_data_model::{ - Authentication, AuthorizationCode, AuthorizationGrant, AuthorizationGrantStage, BrowserSession, - Client, Pkce, Session, User, + AuthorizationCode, AuthorizationGrant, AuthorizationGrantStage, BrowserSession, Client, Pkce, + Session, }; use mas_iana::oauth::PkceCodeChallengeMethod; use oauth2_types::{requests::ResponseMode, scope::Scope}; @@ -151,12 +151,6 @@ struct GrantLookup { oauth2_client_id: Uuid, oauth2_session_id: Option, user_session_id: Option, - user_session_created_at: Option>, - user_id: Option, - user_username: Option, - user_primary_user_email_id: Option, - user_session_last_authentication_id: Option, - user_session_last_authentication_created_at: Option>, } impl GrantLookup { @@ -183,65 +177,20 @@ impl GrantLookup { .row(id) })?; - let last_authentication = match ( - self.user_session_last_authentication_id, - self.user_session_last_authentication_created_at, - ) { - (Some(id), Some(created_at)) => Some(Authentication { - id: id.into(), - created_at, - }), - (None, None) => None, - _ => return Err(DatabaseInconsistencyError::on("user_session_authentications").into()), - }; - - let session = match ( - self.oauth2_session_id, - self.user_session_id, - self.user_session_created_at, - self.user_id, - self.user_username, - self.user_primary_user_email_id, - last_authentication, - ) { - ( - Some(session_id), - Some(user_session_id), - Some(user_session_created_at), - Some(user_id), - Some(user_username), - user_primary_user_email_id, - last_authentication, - ) => { - let user_id = Ulid::from(user_id); - let user = User { - id: user_id, - username: user_username, - sub: user_id.to_string(), - primary_user_email_id: user_primary_user_email_id.map(Into::into), - }; - - let browser_session = BrowserSession { - id: user_session_id.into(), - user, - created_at: user_session_created_at, - finished_at: None, - last_authentication, - }; - - let client = client.clone(); + let session = match (self.oauth2_session_id, self.user_session_id) { + (Some(session_id), Some(user_session_id)) => { let scope = scope.clone(); let session = Session { id: session_id.into(), - client, - browser_session, + client_id: client.id, + user_session_id: user_session_id.into(), scope, }; Some(session) } - (None, None, None, None, None, None, None) => None, + (None, None) => None, _ => { return Err( DatabaseInconsistencyError::on("oauth2_authorization_grants") @@ -394,48 +343,32 @@ pub async fn get_grant_by_id( let res = sqlx::query_as!( GrantLookup, r#" - SELECT - og.oauth2_authorization_grant_id, - og.created_at AS oauth2_authorization_grant_created_at, - og.cancelled_at AS oauth2_authorization_grant_cancelled_at, - og.fulfilled_at AS oauth2_authorization_grant_fulfilled_at, - og.exchanged_at AS oauth2_authorization_grant_exchanged_at, - og.scope AS oauth2_authorization_grant_scope, - og.state AS oauth2_authorization_grant_state, - og.redirect_uri AS oauth2_authorization_grant_redirect_uri, - og.response_mode AS oauth2_authorization_grant_response_mode, - og.nonce AS oauth2_authorization_grant_nonce, - og.max_age AS oauth2_authorization_grant_max_age, - og.oauth2_client_id AS oauth2_client_id, - og.authorization_code AS oauth2_authorization_grant_code, - og.response_type_code AS oauth2_authorization_grant_response_type_code, - og.response_type_id_token AS oauth2_authorization_grant_response_type_id_token, - og.code_challenge AS oauth2_authorization_grant_code_challenge, - og.code_challenge_method AS oauth2_authorization_grant_code_challenge_method, - og.requires_consent AS oauth2_authorization_grant_requires_consent, - os.oauth2_session_id AS "oauth2_session_id?", - us.user_session_id AS "user_session_id?", - us.created_at AS "user_session_created_at?", - u.user_id AS "user_id?", - u.username AS "user_username?", - u.primary_user_email_id AS "user_primary_user_email_id?", - usa.user_session_authentication_id AS "user_session_last_authentication_id?", - usa.created_at AS "user_session_last_authentication_created_at?" + SELECT og.oauth2_authorization_grant_id + , og.created_at AS oauth2_authorization_grant_created_at + , og.cancelled_at AS oauth2_authorization_grant_cancelled_at + , og.fulfilled_at AS oauth2_authorization_grant_fulfilled_at + , og.exchanged_at AS oauth2_authorization_grant_exchanged_at + , og.scope AS oauth2_authorization_grant_scope + , og.state AS oauth2_authorization_grant_state + , og.redirect_uri AS oauth2_authorization_grant_redirect_uri + , og.response_mode AS oauth2_authorization_grant_response_mode + , og.nonce AS oauth2_authorization_grant_nonce + , og.max_age AS oauth2_authorization_grant_max_age + , og.oauth2_client_id AS oauth2_client_id + , og.authorization_code AS oauth2_authorization_grant_code + , og.response_type_code AS oauth2_authorization_grant_response_type_code + , og.response_type_id_token AS oauth2_authorization_grant_response_type_id_token + , og.code_challenge AS oauth2_authorization_grant_code_challenge + , og.code_challenge_method AS oauth2_authorization_grant_code_challenge_method + , og.requires_consent AS oauth2_authorization_grant_requires_consent + , os.oauth2_session_id AS "oauth2_session_id?" + , os.user_session_id AS "user_session_id?" FROM oauth2_authorization_grants og LEFT JOIN oauth2_sessions os USING (oauth2_session_id) - LEFT JOIN user_sessions us - USING (user_session_id) - LEFT JOIN users u - USING (user_id) - LEFT JOIN user_session_authentications usa - USING (user_session_id) WHERE og.oauth2_authorization_grant_id = $1 - - ORDER BY usa.created_at DESC - LIMIT 1 "#, Uuid::from(id), ) @@ -458,48 +391,32 @@ pub async fn lookup_grant_by_code( let res = sqlx::query_as!( GrantLookup, r#" - SELECT - og.oauth2_authorization_grant_id, - og.created_at AS oauth2_authorization_grant_created_at, - og.cancelled_at AS oauth2_authorization_grant_cancelled_at, - og.fulfilled_at AS oauth2_authorization_grant_fulfilled_at, - og.exchanged_at AS oauth2_authorization_grant_exchanged_at, - og.scope AS oauth2_authorization_grant_scope, - og.state AS oauth2_authorization_grant_state, - og.redirect_uri AS oauth2_authorization_grant_redirect_uri, - og.response_mode AS oauth2_authorization_grant_response_mode, - og.nonce AS oauth2_authorization_grant_nonce, - og.max_age AS oauth2_authorization_grant_max_age, - og.oauth2_client_id AS oauth2_client_id, - og.authorization_code AS oauth2_authorization_grant_code, - og.response_type_code AS oauth2_authorization_grant_response_type_code, - og.response_type_id_token AS oauth2_authorization_grant_response_type_id_token, - og.code_challenge AS oauth2_authorization_grant_code_challenge, - og.code_challenge_method AS oauth2_authorization_grant_code_challenge_method, - og.requires_consent AS oauth2_authorization_grant_requires_consent, - os.oauth2_session_id AS "oauth2_session_id?", - us.user_session_id AS "user_session_id?", - us.created_at AS "user_session_created_at?", - u.user_id AS "user_id?", - u.username AS "user_username?", - u.primary_user_email_id AS "user_primary_user_email_id?", - usa.user_session_authentication_id AS "user_session_last_authentication_id?", - usa.created_at AS "user_session_last_authentication_created_at?" + SELECT og.oauth2_authorization_grant_id + , og.created_at AS oauth2_authorization_grant_created_at + , og.cancelled_at AS oauth2_authorization_grant_cancelled_at + , og.fulfilled_at AS oauth2_authorization_grant_fulfilled_at + , og.exchanged_at AS oauth2_authorization_grant_exchanged_at + , og.scope AS oauth2_authorization_grant_scope + , og.state AS oauth2_authorization_grant_state + , og.redirect_uri AS oauth2_authorization_grant_redirect_uri + , og.response_mode AS oauth2_authorization_grant_response_mode + , og.nonce AS oauth2_authorization_grant_nonce + , og.max_age AS oauth2_authorization_grant_max_age + , og.oauth2_client_id AS oauth2_client_id + , og.authorization_code AS oauth2_authorization_grant_code + , og.response_type_code AS oauth2_authorization_grant_response_type_code + , og.response_type_id_token AS oauth2_authorization_grant_response_type_id_token + , og.code_challenge AS oauth2_authorization_grant_code_challenge + , og.code_challenge_method AS oauth2_authorization_grant_code_challenge_method + , og.requires_consent AS oauth2_authorization_grant_requires_consent + , os.oauth2_session_id AS "oauth2_session_id?" + , os.user_session_id AS "user_session_id?" FROM oauth2_authorization_grants og LEFT JOIN oauth2_sessions os USING (oauth2_session_id) - LEFT JOIN user_sessions us - USING (user_session_id) - LEFT JOIN users u - USING (user_id) - LEFT JOIN user_session_authentications usa - USING (user_session_id) WHERE og.authorization_code = $1 - - ORDER BY usa.created_at DESC - LIMIT 1 "#, code, ) @@ -561,8 +478,8 @@ pub async fn derive_session( Ok(Session { id, - browser_session, - client: grant.client.clone(), + user_session_id: browser_session.id, + client_id: grant.client.id, scope: grant.scope.clone(), }) } @@ -573,8 +490,7 @@ pub async fn derive_session( %grant.id, client.id = %grant.client.id, %session.id, - user_session.id = %session.browser_session.id, - user.id = %session.browser_session.user.id, + user_session.id = %session.user_session_id, ), err, )] diff --git a/crates/storage/src/oauth2/mod.rs b/crates/storage/src/oauth2/mod.rs index c0153a3a..66313139 100644 --- a/crates/storage/src/oauth2/mod.rs +++ b/crates/storage/src/oauth2/mod.rs @@ -12,19 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::{BTreeSet, HashMap}; - -use mas_data_model::{BrowserSession, Session, User}; +use mas_data_model::{Session, User}; use sqlx::{PgConnection, PgExecutor, QueryBuilder}; use tracing::{info_span, Instrument}; use ulid::Ulid; use uuid::Uuid; -use self::client::OAuth2ClientRepository; use crate::{ pagination::{process_page, QueryBuilderExt}, - user::BrowserSessionRepository, - Clock, DatabaseError, DatabaseInconsistencyError, Repository, + Clock, DatabaseError, DatabaseInconsistencyError, }; pub mod access_token; @@ -32,14 +28,14 @@ pub mod authorization_grant; pub mod client; pub mod consent; pub mod refresh_token; +pub mod session; #[tracing::instrument( skip_all, fields( %session.id, - user.id = %session.browser_session.user.id, - user_session.id = %session.browser_session.id, - client.id = %session.client.id, + user_session.id = %session.user_session_id, + client.id = %session.client_id, ), err, )] @@ -120,49 +116,10 @@ pub async fn get_paginated_user_oauth_sessions( let (has_previous_page, has_next_page, page) = process_page(page, first, last)?; - let client_ids: BTreeSet = page - .iter() - .map(|i| Ulid::from(i.oauth2_client_id)) - .collect(); - - let browser_session_ids: BTreeSet = - page.iter().map(|i| Ulid::from(i.user_session_id)).collect(); - - let clients = conn.oauth2_client().load_batch(client_ids).await?; - - // TODO: this can generate N queries instead of batching. This is less than - // ideal - let mut browser_sessions: HashMap = HashMap::new(); - for id in browser_session_ids { - let v = conn.browser_session().lookup(id).await?.ok_or_else(|| { - DatabaseInconsistencyError::on("oauth2_sessions").column("user_session_id") - })?; - - browser_sessions.insert(id, v); - } - let page: Result, DatabaseInconsistencyError> = page .into_iter() .map(|item| { let id = Ulid::from(item.oauth2_session_id); - let client = clients - .get(&Ulid::from(item.oauth2_client_id)) - .ok_or_else(|| { - DatabaseInconsistencyError::on("oauth2_sessions") - .column("oauth2_client_id") - .row(id) - })? - .clone(); - - let browser_session = browser_sessions - .get(&Ulid::from(item.user_session_id)) - .ok_or_else(|| { - DatabaseInconsistencyError::on("oauth2_sessions") - .column("user_session_id") - .row(id) - })? - .clone(); - let scope = item.scope.parse().map_err(|e| { DatabaseInconsistencyError::on("oauth2_sessions") .column("scope") @@ -172,8 +129,8 @@ pub async fn get_paginated_user_oauth_sessions( Ok(Session { id: Ulid::from(item.oauth2_session_id), - client, - browser_session, + client_id: item.oauth2_client_id.into(), + user_session_id: item.user_session_id.into(), scope, }) }) diff --git a/crates/storage/src/oauth2/refresh_token.rs b/crates/storage/src/oauth2/refresh_token.rs index 57abf103..bf223a79 100644 --- a/crates/storage/src/oauth2/refresh_token.rs +++ b/crates/storage/src/oauth2/refresh_token.rs @@ -13,22 +13,20 @@ // limitations under the License. use chrono::{DateTime, Utc}; -use mas_data_model::{AccessToken, Authentication, BrowserSession, RefreshToken, Session, User}; +use mas_data_model::{AccessToken, RefreshToken, Session}; use rand::Rng; use sqlx::{PgConnection, PgExecutor}; use ulid::Ulid; use uuid::Uuid; -use super::client::OAuth2ClientRepository; -use crate::{Clock, DatabaseError, DatabaseInconsistencyError, Repository}; +use crate::{Clock, DatabaseError, DatabaseInconsistencyError}; #[tracing::instrument( skip_all, fields( %session.id, - user.id = %session.browser_session.user.id, - user_session.id = %session.browser_session.id, - client.id = %session.client.id, + user_session.id = %session.user_session_id, + client.id = %session.client_id, refresh_token.id, ), err, @@ -82,12 +80,6 @@ struct OAuth2RefreshTokenLookup { oauth2_client_id: Uuid, oauth2_session_scope: String, user_session_id: Uuid, - user_session_created_at: DateTime, - user_id: Uuid, - user_username: String, - user_primary_user_email_id: Option, - user_session_last_authentication_id: Option, - user_session_last_authentication_created_at: Option>, } #[tracing::instrument(skip_all, err)] @@ -99,46 +91,27 @@ pub async fn lookup_active_refresh_token( let res = sqlx::query_as!( OAuth2RefreshTokenLookup, r#" - SELECT - rt.oauth2_refresh_token_id, - rt.refresh_token AS oauth2_refresh_token, - rt.created_at AS oauth2_refresh_token_created_at, - at.oauth2_access_token_id AS "oauth2_access_token_id?", - at.access_token AS "oauth2_access_token?", - at.created_at AS "oauth2_access_token_created_at?", - at.expires_at AS "oauth2_access_token_expires_at?", - os.oauth2_session_id AS "oauth2_session_id!", - os.oauth2_client_id AS "oauth2_client_id!", - os.scope AS "oauth2_session_scope!", - us.user_session_id AS "user_session_id!", - us.created_at AS "user_session_created_at!", - u.user_id AS "user_id!", - u.username AS "user_username!", - u.primary_user_email_id AS "user_primary_user_email_id", - usa.user_session_authentication_id AS "user_session_last_authentication_id?", - usa.created_at AS "user_session_last_authentication_created_at?" + SELECT rt.oauth2_refresh_token_id + , rt.refresh_token AS oauth2_refresh_token + , rt.created_at AS oauth2_refresh_token_created_at + , at.oauth2_access_token_id AS "oauth2_access_token_id?" + , at.access_token AS "oauth2_access_token?" + , at.created_at AS "oauth2_access_token_created_at?" + , at.expires_at AS "oauth2_access_token_expires_at?" + , os.oauth2_session_id AS "oauth2_session_id!" + , os.oauth2_client_id AS "oauth2_client_id!" + , os.scope AS "oauth2_session_scope!" + , os.user_session_id AS "user_session_id!" FROM oauth2_refresh_tokens rt INNER JOIN oauth2_sessions os USING (oauth2_session_id) LEFT JOIN oauth2_access_tokens at USING (oauth2_access_token_id) - INNER JOIN user_sessions us - USING (user_session_id) - INNER JOIN users u - USING (user_id) - LEFT JOIN user_session_authentications usa - USING (user_session_id) - LEFT JOIN user_emails ue - ON ue.user_email_id = u.primary_user_email_id WHERE rt.refresh_token = $1 AND rt.consumed_at IS NULL AND rt.revoked_at IS NULL - AND us.finished_at IS NULL AND os.finished_at IS NULL - - ORDER BY usa.created_at DESC - LIMIT 1 "#, token, ) @@ -173,44 +146,6 @@ pub async fn lookup_active_refresh_token( }; let session_id = res.oauth2_session_id.into(); - let client = conn - .oauth2_client() - .lookup(res.oauth2_client_id.into()) - .await? - .ok_or_else(|| { - DatabaseInconsistencyError::on("oauth2_sessions") - .column("client_id") - .row(session_id) - })?; - - let user_id = Ulid::from(res.user_id); - let user = User { - id: user_id, - username: res.user_username, - sub: user_id.to_string(), - primary_user_email_id: res.user_primary_user_email_id.map(Into::into), - }; - - let last_authentication = match ( - res.user_session_last_authentication_id, - res.user_session_last_authentication_created_at, - ) { - (None, None) => None, - (Some(id), Some(created_at)) => Some(Authentication { - id: id.into(), - created_at, - }), - _ => return Err(DatabaseInconsistencyError::on("user_session_authentications").into()), - }; - - let browser_session = BrowserSession { - id: res.user_session_id.into(), - created_at: res.user_session_created_at, - finished_at: None, - user, - last_authentication, - }; - let scope = res.oauth2_session_scope.parse().map_err(|e| { DatabaseInconsistencyError::on("oauth2_sessions") .column("scope") @@ -220,8 +155,8 @@ pub async fn lookup_active_refresh_token( let session = Session { id: session_id, - client, - browser_session, + client_id: res.oauth2_client_id.into(), + user_session_id: res.user_session_id.into(), scope, }; diff --git a/crates/storage/src/oauth2/session.rs b/crates/storage/src/oauth2/session.rs new file mode 100644 index 00000000..71efb5bd --- /dev/null +++ b/crates/storage/src/oauth2/session.rs @@ -0,0 +1,20 @@ +// Copyright 2022 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. + +use async_trait::async_trait; + +#[async_trait] +pub trait OAuth2SessionRepository { + type Error; +}