Give refresh tokens the same invalidation treatment as access tokens
This commit is contained in:
parent
c5eef85e6b
commit
2d23eae641
14
.sqlx/query-623516eda004bf65b650978e95c6fbc1f29651cb68c2c44241f2b69a7de31120.json
generated
Normal file
14
.sqlx/query-623516eda004bf65b650978e95c6fbc1f29651cb68c2c44241f2b69a7de31120.json
generated
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n DELETE FROM application_refresh_tokens WHERE refresh_token_hash = $1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Bytea"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "623516eda004bf65b650978e95c6fbc1f29651cb68c2c44241f2b69a7de31120"
|
||||
}
|
11
src/store.rs
11
src/store.rs
@ -85,8 +85,8 @@ impl<'a, 'txn> IdCoopStoreTxn<'a, 'txn> {
|
||||
pub async fn invalidate_access_token_by_hash(
|
||||
&mut self,
|
||||
access_token_hash: &[u8],
|
||||
refresh_token_hash: &[u8],
|
||||
) -> eyre::Result<()> {
|
||||
// TODO presumably we also want to do the same for refresh tokens
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM application_access_tokens WHERE access_token_hash = $1
|
||||
@ -96,6 +96,15 @@ impl<'a, 'txn> IdCoopStoreTxn<'a, 'txn> {
|
||||
.execute(&mut **self.txn)
|
||||
.await
|
||||
.context("failed to invalidate access token by hash")?;
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM application_refresh_tokens WHERE refresh_token_hash = $1
|
||||
",
|
||||
refresh_token_hash
|
||||
)
|
||||
.execute(&mut **self.txn)
|
||||
.await
|
||||
.context("failed to invalidate refresh token by hash")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -64,6 +64,7 @@ pub struct RedeemedAuthCode {
|
||||
/// The access token hash of whoever redeemed the auth code.
|
||||
/// Is used to invalidate the access token if an auth code is double-redeemed.
|
||||
access_token_hash: AccessTokenHash,
|
||||
refresh_token_hash: RefreshTokenHash,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -84,10 +85,12 @@ impl VolatileCodeStoreInner {
|
||||
&mut self,
|
||||
auth_code: &AuthCode,
|
||||
access_token_hash: AccessTokenHash,
|
||||
refresh_token_hash: RefreshTokenHash,
|
||||
) -> CodeRedemption {
|
||||
if let Some(conflicted) = self.conflictable_codes.get(auth_code) {
|
||||
return CodeRedemption::Conflicted {
|
||||
access_token_to_invalidate: conflicted.access_token_hash.clone(),
|
||||
refresh_token_to_invalidate: conflicted.refresh_token_hash.clone(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -95,8 +98,13 @@ impl VolatileCodeStoreInner {
|
||||
return CodeRedemption::Invalid;
|
||||
};
|
||||
|
||||
self.conflictable_codes
|
||||
.insert(auth_code, RedeemedAuthCode { access_token_hash });
|
||||
self.conflictable_codes.insert(
|
||||
auth_code,
|
||||
RedeemedAuthCode {
|
||||
access_token_hash,
|
||||
refresh_token_hash,
|
||||
},
|
||||
);
|
||||
|
||||
CodeRedemption::Valid { binding }
|
||||
}
|
||||
@ -186,9 +194,10 @@ impl VolatileCodeStore {
|
||||
&self,
|
||||
auth_code: &AuthCode,
|
||||
access_token_hash: AccessTokenHash,
|
||||
refresh_token_hash: RefreshTokenHash,
|
||||
) -> CodeRedemption {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner.redeem(auth_code, access_token_hash)
|
||||
inner.redeem(auth_code, access_token_hash, refresh_token_hash)
|
||||
}
|
||||
|
||||
pub fn add_redeemable(&self, auth_code: AuthCode, binding: AuthCodeBinding, expires_at: u64) {
|
||||
@ -204,6 +213,8 @@ pub enum CodeRedemption {
|
||||
Valid { binding: AuthCodeBinding },
|
||||
/// That auth code had already been redeemed: please invalidate the given access token and reject this redemption.
|
||||
Conflicted {
|
||||
// TODO what if the token was refreshed since?
|
||||
access_token_to_invalidate: AccessTokenHash,
|
||||
refresh_token_to_invalidate: RefreshTokenHash,
|
||||
},
|
||||
}
|
||||
|
@ -176,7 +176,11 @@ pub async fn oidc_token(
|
||||
let refresh_token_hash: RefreshTokenHash = Blake2s256::digest(&refresh_token).into();
|
||||
|
||||
// Redeem the auth code so we can check it and then maybe issue an access token.
|
||||
let binding = match code_store.redeem(&auth_code, access_token_hash.clone()) {
|
||||
let binding = match code_store.redeem(
|
||||
&auth_code,
|
||||
access_token_hash.clone(),
|
||||
refresh_token_hash.clone(),
|
||||
) {
|
||||
CodeRedemption::Invalid => {
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
@ -190,14 +194,18 @@ pub async fn oidc_token(
|
||||
CodeRedemption::Valid { binding } => binding,
|
||||
CodeRedemption::Conflicted {
|
||||
access_token_to_invalidate,
|
||||
refresh_token_to_invalidate,
|
||||
} => {
|
||||
// Invalidate the access token that was issued before
|
||||
if let Err(err) = store
|
||||
.txn(move |mut txn| {
|
||||
Box::pin(async move {
|
||||
txn.invalidate_access_token_by_hash(&access_token_to_invalidate)
|
||||
.await
|
||||
.context("failed to invalidate access token")
|
||||
txn.invalidate_access_token_by_hash(
|
||||
&access_token_to_invalidate,
|
||||
&refresh_token_to_invalidate,
|
||||
)
|
||||
.await
|
||||
.context("failed to invalidate access token")
|
||||
})
|
||||
})
|
||||
.await
|
||||
|
Loading…
Reference in New Issue
Block a user