Skip to content

Commit

Permalink
feat: log error responses returned by Zitadel (#584)
Browse files Browse the repository at this point in the history
When we receive an error response on an introspection call, for example
a 503, we previously returned a ParseError as the response was not valid
JSON. This change adds a new error type ZitadelResponseError which
handles such cases by logging the status code and body of the error
response. This is a breaking change as we add a Variant to the error
enum which needs to be handled by the user.

Co-authored-by: Jakob Herpel <[email protected]>
Co-authored-by: Christoph Bühler <[email protected]>
  • Loading branch information
3 people authored Jan 15, 2025
1 parent e550ba3 commit de1c389
Showing 1 changed file with 30 additions and 1 deletion.
31 changes: 30 additions & 1 deletion src/oidc/introspection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ use custom_error::custom_error;
use openidconnect::http::Method;
use openidconnect::reqwest::async_http_client;
use openidconnect::url::{ParseError, Url};
use openidconnect::HttpResponse;
use openidconnect::{
core::CoreTokenType, ExtraTokenFields, HttpRequest, StandardTokenIntrospectionResponse,
};

use reqwest::header::{HeaderMap, ACCEPT, AUTHORIZATION, CONTENT_TYPE};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt::Debug;
use std::error::Error;
use std::fmt::{Debug, Display};

use crate::credentials::{Application, ApplicationError};

Expand All @@ -25,6 +27,7 @@ custom_error! {
ParseUrl{source: ParseError} = "could not parse url: {source}",
ParseResponse{source: serde_json::Error} = "could not parse introspection response: {source}",
DecodeResponse{source: base64::DecodeError} = "could not decode base64 metadata: {source}",
ResponseError{source: ZitadelResponseError} = "received error response from Zitadel: {source}",
}

/// Introspection response information that is returned by the ZITADEL
Expand Down Expand Up @@ -221,13 +224,39 @@ pub async fn introspect(
.await
.map_err(|source| IntrospectionError::RequestFailed { source })?;

if !response.status_code.is_success() {
return Err(IntrospectionError::ResponseError {
source: ZitadelResponseError::from_response(&response),
});
}

let mut response: ZitadelIntrospectionResponse =
serde_json::from_slice(response.body.as_slice())
.map_err(|source| IntrospectionError::ParseResponse { source })?;
decode_metadata(&mut response)?;
Ok(response)
}

#[derive(Debug)]
struct ZitadelResponseError {
status_code: String,
body: String,
}
impl ZitadelResponseError {
fn from_response(response: &HttpResponse) -> Self {
Self {
status_code: response.status_code.to_string(),
body: String::from_utf8_lossy(response.body.as_slice()).to_string(),
}
}
}
impl Display for ZitadelResponseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "status code: {}, body: {}", self.status_code, self.body)
}
}
impl Error for ZitadelResponseError {}

// Metadata values are base64 encoded.
fn decode_metadata(response: &mut ZitadelIntrospectionResponse) -> Result<(), IntrospectionError> {
if let Some(h) = &response.extra_fields().metadata {
Expand Down

0 comments on commit de1c389

Please sign in to comment.