diff --git a/crates/handlers/src/graphql/tests.rs b/crates/handlers/src/graphql/tests.rs index fb17fbee..d6b0103c 100644 --- a/crates/handlers/src/graphql/tests.rs +++ b/crates/handlers/src/graphql/tests.rs @@ -90,7 +90,7 @@ async fn start_oauth_session( let session = repo .oauth2_session() - .add(&mut rng, &state.clock, client, &browser_session, scope) + .add_from_browser_session(&mut rng, &state.clock, client, &browser_session, scope) .await .unwrap(); diff --git a/crates/handlers/src/oauth2/authorization/complete.rs b/crates/handlers/src/oauth2/authorization/complete.rs index 48b1800c..c804c304 100644 --- a/crates/handlers/src/oauth2/authorization/complete.rs +++ b/crates/handlers/src/oauth2/authorization/complete.rs @@ -244,7 +244,7 @@ pub(crate) async fn complete( // All good, let's start the session let session = repo .oauth2_session() - .add(rng, clock, client, browser_session, grant.scope.clone()) + .add_from_browser_session(rng, clock, client, browser_session, grant.scope.clone()) .await?; let grant = repo diff --git a/crates/handlers/src/oauth2/introspection.rs b/crates/handlers/src/oauth2/introspection.rs index a627c721..7aed08df 100644 --- a/crates/handlers/src/oauth2/introspection.rs +++ b/crates/handlers/src/oauth2/introspection.rs @@ -443,7 +443,7 @@ mod tests { let session = repo .oauth2_session() - .add( + .add_from_browser_session( &mut state.rng(), &state.clock, &client, diff --git a/crates/handlers/src/oauth2/revoke.rs b/crates/handlers/src/oauth2/revoke.rs index 7731cbd4..b969d387 100644 --- a/crates/handlers/src/oauth2/revoke.rs +++ b/crates/handlers/src/oauth2/revoke.rs @@ -302,7 +302,7 @@ mod tests { let session = repo .oauth2_session() - .add( + .add_from_browser_session( &mut state.rng(), &state.clock, &client, @@ -369,7 +369,7 @@ mod tests { let mut repo = state.repository().await.unwrap(); let session = repo .oauth2_session() - .add( + .add_from_browser_session( &mut state.rng(), &state.clock, &client, diff --git a/crates/handlers/src/oauth2/token.rs b/crates/handlers/src/oauth2/token.rs index a5fa5e4b..4dc1667e 100644 --- a/crates/handlers/src/oauth2/token.rs +++ b/crates/handlers/src/oauth2/token.rs @@ -506,7 +506,7 @@ mod tests { let session = repo .oauth2_session() - .add( + .add_from_browser_session( &mut state.rng(), &state.clock, &client, @@ -606,7 +606,7 @@ mod tests { let session = repo .oauth2_session() - .add( + .add_from_browser_session( &mut state.rng(), &state.clock, &client, @@ -691,7 +691,7 @@ mod tests { // Get a token pair let session = repo .oauth2_session() - .add( + .add_from_browser_session( &mut state.rng(), &state.clock, &client, diff --git a/crates/storage-pg/.sqlx/query-6554d3620a5f7fb0e85af44e8a21c2f2f3ebe4b805ec67aca4a2278a8ae16693.json b/crates/storage-pg/.sqlx/query-6554d3620a5f7fb0e85af44e8a21c2f2f3ebe4b805ec67aca4a2278a8ae16693.json new file mode 100644 index 00000000..492e1632 --- /dev/null +++ b/crates/storage-pg/.sqlx/query-6554d3620a5f7fb0e85af44e8a21c2f2f3ebe4b805ec67aca4a2278a8ae16693.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO oauth2_sessions\n ( oauth2_session_id\n , oauth2_client_id\n , scope_list\n , created_at\n )\n VALUES ($1, $2, $3, $4)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Uuid", + "TextArray", + "Timestamptz" + ] + }, + "nullable": [] + }, + "hash": "6554d3620a5f7fb0e85af44e8a21c2f2f3ebe4b805ec67aca4a2278a8ae16693" +} diff --git a/crates/storage-pg/src/oauth2/mod.rs b/crates/storage-pg/src/oauth2/mod.rs index f73380d5..6475df29 100644 --- a/crates/storage-pg/src/oauth2/mod.rs +++ b/crates/storage-pg/src/oauth2/mod.rs @@ -211,7 +211,7 @@ mod tests { // Create an OAuth session let session = repo .oauth2_session() - .add( + .add_from_browser_session( &mut rng, &clock, &client, @@ -464,28 +464,28 @@ mod tests { // we're getting consistent ordering in lists. let session11 = repo .oauth2_session() - .add(&mut rng, &clock, &client1, &user1_session, scope.clone()) + .add_from_browser_session(&mut rng, &clock, &client1, &user1_session, scope.clone()) .await .unwrap(); clock.advance(Duration::minutes(1)); let session12 = repo .oauth2_session() - .add(&mut rng, &clock, &client1, &user2_session, scope.clone()) + .add_from_browser_session(&mut rng, &clock, &client1, &user2_session, scope.clone()) .await .unwrap(); clock.advance(Duration::minutes(1)); let session21 = repo .oauth2_session() - .add(&mut rng, &clock, &client2, &user1_session, scope2.clone()) + .add_from_browser_session(&mut rng, &clock, &client2, &user1_session, scope2.clone()) .await .unwrap(); clock.advance(Duration::minutes(1)); let session22 = repo .oauth2_session() - .add(&mut rng, &clock, &client2, &user2_session, scope2.clone()) + .add_from_browser_session(&mut rng, &clock, &client2, &user2_session, scope2.clone()) .await .unwrap(); clock.advance(Duration::minutes(1)); diff --git a/crates/storage-pg/src/oauth2/session.rs b/crates/storage-pg/src/oauth2/session.rs index 98de3edd..64ff7c9f 100644 --- a/crates/storage-pg/src/oauth2/session.rs +++ b/crates/storage-pg/src/oauth2/session.rs @@ -133,7 +133,7 @@ impl<'c> OAuth2SessionRepository for PgOAuth2SessionRepository<'c> { } #[tracing::instrument( - name = "db.oauth2_session.add", + name = "db.oauth2_session.add_from_browser_session", skip_all, fields( db.statement, @@ -145,7 +145,7 @@ impl<'c> OAuth2SessionRepository for PgOAuth2SessionRepository<'c> { ), err, )] - async fn add( + async fn add_from_browser_session( &mut self, rng: &mut (dyn RngCore + Send), clock: &dyn Clock, @@ -193,6 +193,60 @@ impl<'c> OAuth2SessionRepository for PgOAuth2SessionRepository<'c> { }) } + #[tracing::instrument( + name = "db.oauth2_session.add_from_client_credentials", + skip_all, + fields( + db.statement, + %client.id, + session.id, + session.scope = %scope, + ), + err, + )] + async fn add_from_client_credentials( + &mut self, + rng: &mut (dyn RngCore + Send), + clock: &dyn Clock, + client: &Client, + scope: Scope, + ) -> Result { + let created_at = clock.now(); + let id = Ulid::from_datetime_with_source(created_at.into(), rng); + tracing::Span::current().record("session.id", tracing::field::display(id)); + + let scope_list: Vec = scope.iter().map(|s| s.as_str().to_owned()).collect(); + + sqlx::query!( + r#" + INSERT INTO oauth2_sessions + ( oauth2_session_id + , oauth2_client_id + , scope_list + , created_at + ) + VALUES ($1, $2, $3, $4) + "#, + Uuid::from(id), + Uuid::from(client.id), + &scope_list, + created_at, + ) + .traced() + .execute(&mut *self.conn) + .await?; + + Ok(Session { + id, + state: SessionState::Valid, + created_at, + user_id: None, + user_session_id: None, + client_id: client.id, + scope, + }) + } + #[tracing::instrument( name = "db.oauth2_session.finish", skip_all, diff --git a/crates/storage/src/oauth2/session.rs b/crates/storage/src/oauth2/session.rs index a3247f55..1a2613ec 100644 --- a/crates/storage/src/oauth2/session.rs +++ b/crates/storage/src/oauth2/session.rs @@ -140,7 +140,7 @@ pub trait OAuth2SessionRepository: Send + Sync { /// Returns [`Self::Error`] if the underlying repository fails async fn lookup(&mut self, id: Ulid) -> Result, Self::Error>; - /// Create a new [`Session`] + /// Create a new [`Session`] out of a [`Client`] and a [`BrowserSession`] /// /// Returns the newly created [`Session`] /// @@ -156,7 +156,7 @@ pub trait OAuth2SessionRepository: Send + Sync { /// # Errors /// /// Returns [`Self::Error`] if the underlying repository fails - async fn add( + async fn add_from_browser_session( &mut self, rng: &mut (dyn RngCore + Send), clock: &dyn Clock, @@ -165,6 +165,29 @@ pub trait OAuth2SessionRepository: Send + Sync { scope: Scope, ) -> Result; + /// Create a new [`Session`] for a [`Client`] using the client credentials + /// flow + /// + /// Returns the newly created [`Session`] + /// + /// # Parameters + /// + /// * `rng`: The random number generator to use + /// * `clock`: The clock used to generate timestamps + /// * `client`: The [`Client`] which created the [`Session`] + /// * `scope`: The [`Scope`] of the [`Session`] + /// + /// # Errors + /// + /// Returns [`Self::Error`] if the underlying repository fails + async fn add_from_client_credentials( + &mut self, + rng: &mut (dyn RngCore + Send), + clock: &dyn Clock, + client: &Client, + scope: Scope, + ) -> Result; + /// Mark a [`Session`] as finished /// /// Returns the updated [`Session`] @@ -211,7 +234,7 @@ pub trait OAuth2SessionRepository: Send + Sync { repository_impl!(OAuth2SessionRepository: async fn lookup(&mut self, id: Ulid) -> Result, Self::Error>; - async fn add( + async fn add_from_browser_session( &mut self, rng: &mut (dyn RngCore + Send), clock: &dyn Clock, @@ -220,6 +243,14 @@ repository_impl!(OAuth2SessionRepository: scope: Scope, ) -> Result; + async fn add_from_client_credentials( + &mut self, + rng: &mut (dyn RngCore + Send), + clock: &dyn Clock, + client: &Client, + scope: Scope, + ) -> Result; + async fn finish(&mut self, clock: &dyn Clock, session: Session) -> Result;