Merge Pull Request #5 "feat: Handle Grant error while refreshing token"

Expired refresh_tokens weren't removed from the session causing a 500
Internal Server Error.
This PR removes the invalid request token when it is expired.
This commit is contained in:
Paul Zinselmeyer 2024-01-23 14:17:55 +01:00
commit 8d0be319ad
Signed by: pfzetto
GPG key ID: B471A1AF06C895FD

View file

@ -15,9 +15,12 @@ use tower_service::Service;
use tower_sessions::Session; use tower_sessions::Session;
use openidconnect::{ use openidconnect::{
core::CoreAuthenticationFlow, reqwest::async_http_client, AccessTokenHash, AuthorizationCode, core::{CoreAuthenticationFlow, CoreErrorResponseType},
CsrfToken, Nonce, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, Scope, reqwest::async_http_client,
TokenResponse, AccessTokenHash, AuthorizationCode, CsrfToken, Nonce, OAuth2TokenResponse, PkceCodeChallenge,
PkceCodeVerifier, RedirectUrl,
RequestTokenError::ServerResponse,
Scope, TokenResponse,
}; };
use crate::{ use crate::{
@ -343,39 +346,53 @@ where
refresh_request.add_scope(Scope::new(scope.to_string())); refresh_request.add_scope(Scope::new(scope.to_string()));
} }
let token_response = match refresh_request.request_async(async_http_client).await {
refresh_request.request_async(async_http_client).await?; Ok(token_response) => {
// Extract the ID token claims after verifying its authenticity and nonce.
let id_token = token_response
.id_token()
.ok_or(MiddlewareError::IdTokenMissing)?;
let claims = id_token.claims(
&oidcclient.client.id_token_verifier(),
&login_session.nonce,
)?;
// Extract the ID token claims after verifying its authenticity and nonce. // Verify the access token hash to ensure that the access token hasn't been substituted for
let id_token = token_response // another user's.
.id_token() if let Some(expected_access_token_hash) = claims.access_token_hash()
.ok_or(MiddlewareError::IdTokenMissing)?; {
let claims = id_token let actual_access_token_hash = AccessTokenHash::from_token(
.claims(&oidcclient.client.id_token_verifier(), &login_session.nonce)?; token_response.access_token(),
&id_token.signing_alg()?,
)?;
if actual_access_token_hash != *expected_access_token_hash {
return Err(MiddlewareError::AccessTokenHashInvalid);
}
}
// Verify the access token hash to ensure that the access token hasn't been substituted for login_session.id_token = Some(id_token.to_string());
// another user's. login_session.access_token =
if let Some(expected_access_token_hash) = claims.access_token_hash() { Some(token_response.access_token().secret().to_string());
let actual_access_token_hash = AccessTokenHash::from_token( login_session.refresh_token = token_response
token_response.access_token(), .refresh_token()
&id_token.signing_alg()?, .map(|x| x.secret().to_string());
)?;
if actual_access_token_hash != *expected_access_token_hash { parts.extensions.insert(OidcClaims(claims.clone()));
return Err(MiddlewareError::AccessTokenHashInvalid); parts.extensions.insert(OidcAccessToken(
login_session.access_token.clone().unwrap_or_default(),
));
} }
} Err(ServerResponse(e))
if *e.error() == CoreErrorResponseType::InvalidGrant =>
login_session.id_token = Some(id_token.to_string()); {
login_session.access_token = // Refresh failed, refresh_token most likely expired or
Some(token_response.access_token().secret().to_string()); // invalid, the session can be considered lost
login_session.refresh_token = token_response login_session.refresh_token = None;
.refresh_token() }
.map(|x| x.secret().to_string()); Err(err) => {
return Err(err.into());
parts.extensions.insert(OidcClaims(claims.clone())); }
parts.extensions.insert(OidcAccessToken( };
login_session.access_token.clone().unwrap_or_default(),
));
let session = parts let session = parts
.extensions .extensions