diff --git a/crates/core/sqlx-data.json b/crates/core/sqlx-data.json index 396f585c..28ed1568 100644 --- a/crates/core/sqlx-data.json +++ b/crates/core/sqlx-data.json @@ -661,6 +661,18 @@ ] } }, + "eaddc1e33715ad31b4195fda72dbe870f179dd8da53a88d0543b72a278ed1d3d": { + "query": "\n DELETE FROM oauth2_codes\n WHERE id = $1\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [] + } + }, "f9a09ff53b6f221649f4f050e3d5ade114f852ddf50a78610a6c0ef0689af681": { "query": "\n INSERT INTO users (username, hashed_password)\n VALUES ($1, $2)\n RETURNING id\n ", "describe": { diff --git a/crates/core/src/handlers/oauth2/token.rs b/crates/core/src/handlers/oauth2/token.rs index 8e22fc0a..0761df58 100644 --- a/crates/core/src/handlers/oauth2/token.rs +++ b/crates/core/src/handlers/oauth2/token.rs @@ -47,7 +47,7 @@ use crate::{ }, storage::oauth2::{ access_token::{add_access_token, revoke_access_token}, - authorization_code::lookup_code, + authorization_code::{consume_code, lookup_code}, refresh_token::{add_refresh_token, lookup_refresh_token, replace_refresh_token}, }, tokens, @@ -154,13 +154,13 @@ async fn authorization_code_grant( conn: &mut PoolConnection, ) -> Result { let mut txn = conn.begin().await.wrap_error()?; + // TODO: recover from failed code lookup with invalid_grant instead let code = lookup_code(&mut txn, &grant.code).await.wrap_error()?; if client.client_id != code.client_id { return error(UnauthorizedClient); } // TODO: verify PKCE - // TODO: make the code invalid let ttl = Duration::minutes(5); let (access_token, refresh_token) = { let mut rng = thread_rng(); @@ -208,6 +208,8 @@ async fn authorization_code_grant( .with_refresh_token(refresh_token.token) .with_id_token(id_token); + consume_code(&mut txn, code.id).await.wrap_error()?; + txn.commit().await.wrap_error()?; Ok(params) diff --git a/crates/core/src/storage/oauth2/authorization_code.rs b/crates/core/src/storage/oauth2/authorization_code.rs index 4ab8a348..a6437716 100644 --- a/crates/core/src/storage/oauth2/authorization_code.rs +++ b/crates/core/src/storage/oauth2/authorization_code.rs @@ -90,3 +90,29 @@ pub async fn lookup_code( .await .context("could not lookup oauth2 code") } + +pub async fn consume_code( + executor: impl Executor<'_, Database = Postgres>, + code_id: i64, +) -> anyhow::Result<()> { + // TODO: mark the code as invalid instead to allow invalidating the whole + // session on code reuse + let res = sqlx::query!( + r#" + DELETE FROM oauth2_codes + WHERE id = $1 + "#, + code_id, + ) + .execute(executor) + .await + .context("could not consume authorization code")?; + + if res.rows_affected() == 1 { + Ok(()) + } else { + Err(anyhow::anyhow!( + "no row were affected when consuming authorization code" + )) + } +}