Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ async-trait = { version = "0.1.80", optional = true }
axum = { version = "0.8.0", optional = true, features = ["macros"] }
axum-extra = { version = "0.12.0", optional = true, features = ["typed-header"] }
base64-compat = { version = "1", optional = true }
custom_error = "1.9.2"
document-features = { version = "0.2.8", optional = true }
jsonwebtoken = { version = "10.3.0", optional = true, features = ["aws_lc_rs"] }
moka = { version = "0.12.8", features = ["future"], optional = true }
Expand All @@ -165,6 +164,7 @@ rocket = { version = "0.5.0", optional = true }
serde = { version = "1.0.200", features = ["derive"], optional = true }
serde_json = { version = "1.0.116", optional = true }
serde_urlencoded = { version = "0.7.1", optional = true }
thiserror = "2.0.19"
time = { version = "0.3.36", optional = true }
tokio = { version = "1.37.0", optional = true, features = [
"macros",
Expand Down
2 changes: 1 addition & 1 deletion examples/axum_webapi_oauth_interception_basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ async fn authed(user: IntrospectedUser) -> impl IntoResponse {
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
let is = IntrospectionStateBuilder::new("https://zitadel-libraries-l8boqa.zitadel.cloud")
.with_basic_auth(
"194339055499018497@zitadel_rust_test",
Expand Down
2 changes: 1 addition & 1 deletion examples/fetch_profile_with_pat.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use zitadel::api::{clients::ClientBuilder, zitadel::auth::v1::GetMyUserRequest};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
const PERSONAL_ACCESS_TOKEN: &str =
"dEnGhIFs3VnqcQU5D2zRSeiarB1nwH6goIKY0J8MWZbsnWcTuu1C59lW9DgCq1y096GYdXA";
const ZITADEL_URL: &str = "https://zitadel-libraries-l8boqa.zitadel.cloud";
Expand Down
2 changes: 1 addition & 1 deletion examples/fetch_profile_with_service_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use zitadel::{
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
const SERVICE_ACCOUNT: &str = r#"
{
"type": "serviceaccount",
Expand Down
2 changes: 1 addition & 1 deletion examples/service_account_authentication.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use zitadel::credentials::ServiceAccount;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
const SERVICE_ACCOUNT: &str = r#"
{
"type": "serviceaccount",
Expand Down
27 changes: 16 additions & 11 deletions src/actix/introspection/config_builder.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
use custom_error::custom_error;

use crate::actix::introspection::config::IntrospectionConfig;
use crate::credentials::Application;
use crate::oidc::discovery::{discover, DiscoveryError};
use crate::oidc::introspection::AuthorityAuthentication;

custom_error! {
/// Error type for introspection config builder related errors.
pub IntrospectionConfigBuilderError
NoAuthSchema = "no authentication for authority defined",
Discovery{source: DiscoveryError} = "could not fetch discovery document: {source}",
NoIntrospectionUrl = "discovery document did not contain an introspection url",
use thiserror::Error;

/// Error type for introspection config builder related errors.
#[derive(Debug, Error)]
pub enum IntrospectionConfigBuilderError {
#[error("no authentication for authority defined")]
NoAuthSchema,
#[error("could not fetch discovery document: {source}")]
Discovery {
#[from]
source: DiscoveryError,
},
#[error("discovery document did not contain an introspection url")]
NoIntrospectionUrl,
}

/// Builder for [IntrospectionConfig]s.
Expand Down Expand Up @@ -74,7 +79,7 @@ impl IntrospectionConfigBuilder {
///
/// ```
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn std::error::Error>>{
/// # async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>>{
/// # use zitadel::credentials::Application;
/// # use zitadel::actix::introspection::IntrospectionConfigBuilder;
/// # const APPLICATION: &str = r#"
Expand All @@ -99,7 +104,7 @@ impl IntrospectionConfigBuilder {
///
/// ```
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn std::error::Error>>{
/// # async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>>{
/// # use zitadel::actix::introspection::IntrospectionConfigBuilder;
/// let config = IntrospectionConfigBuilder::new("https://zitadel-libraries-l8boqa.zitadel.cloud")
/// .with_basic_auth(
Expand Down
39 changes: 24 additions & 15 deletions src/actix/introspection/extractor.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
use std::{future::Future, pin::Pin};

use crate::actix::introspection::config::IntrospectionConfig;
use crate::oidc::introspection::{introspect, IntrospectionError, ZitadelIntrospectionResponse};
use actix_web::dev::Payload;
use actix_web::error::{ErrorInternalServerError, ErrorUnauthorized};
use actix_web::{Error, FromRequest, HttpRequest};
use custom_error::custom_error;
use openidconnect::TokenIntrospectionResponse;
use std::collections::HashMap;

use crate::actix::introspection::config::IntrospectionConfig;
use crate::oidc::introspection::{introspect, IntrospectionError, ZitadelIntrospectionResponse};

custom_error! {
/// Error type for extractor related errors.
pub IntrospectionExtractorError
MissingConfig = "no introspection config given to actix app data",
Unauthorized = "no HTTP authorization header found",
InvalidHeader = "authorization header is invalid",
WrongScheme = "Authorization header is not a bearer token",
Introspection{source: IntrospectionError} = "introspection returned an error: {source}",
Inactive = "access token is inactive",
NoUserId = "introspection result contained no user id",
use thiserror::Error;

/// Error type for extractor related errors.
#[derive(Debug, Error)]
pub enum IntrospectionExtractorError {
#[error("no introspection config given to actix app data")]
MissingConfig,
#[error("no HTTP authorization header found")]
Unauthorized,
#[error("authorization header is invalid")]
InvalidHeader,
#[error("Authorization header is not a bearer token")]
WrongScheme,
#[error("introspection returned an error: {source}")]
Introspection {
#[from]
source: IntrospectionError,
},
#[error("access token is inactive")]
Inactive,
#[error("introspection result contained no user id")]
NoUserId,
}

/// Struct for the handler function that requires an authenticated user.
Expand Down
44 changes: 27 additions & 17 deletions src/api/clients.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
//! specific interceptors for authentication.

use std::error::Error;

use custom_error::custom_error;
use thiserror::Error;
use tonic::codegen::{Body, Bytes, InterceptedService, StdError};
use tonic::service::Interceptor;

Expand Down Expand Up @@ -37,12 +36,15 @@ use crate::api::zitadel::system::v1::system_service_client::SystemServiceClient;
#[cfg(feature = "interceptors")]
use crate::credentials::{AuthenticationOptions, ServiceAccount};

custom_error! {
/// Errors that may occur when creating a client.
pub ClientError
InvalidUrl = "the provided url is invalid",
ConnectionError = "could not connect to provided endpoint",
TlsInitializationError = "could not setup tls connection",
/// Errors that may occur when creating a client.
#[derive(Debug, Error)]
pub enum ClientError {
#[error("the provided url is invalid")]
InvalidUrl,
#[error("could not connect to provided endpoint")]
ConnectionError,
#[error("could not setup tls connection")]
TlsInitializationError,
}

/// A builder to create configured gRPC clients for ZITADEL API access.
Expand Down Expand Up @@ -146,7 +148,9 @@ where
/// cannot be parsed into a valid URL or if the connection to the endpoint
/// is not possible.
#[cfg(feature = "api-admin-v1")]
pub async fn build_admin_client(self) -> Result<AdminServiceClient<T::Target>, Box<dyn Error>> {
pub async fn build_admin_client(
self,
) -> Result<AdminServiceClient<T::Target>, Box<dyn Error + Send + Sync + 'static>> {
let channel = self
.interceptor
.build_service(get_channel(&self.api_endpoint).await?);
Expand All @@ -161,7 +165,9 @@ where
/// cannot be parsed into a valid URL or if the connection to the endpoint
/// is not possible.
#[cfg(feature = "api-auth-v1")]
pub async fn build_auth_client(self) -> Result<AuthServiceClient<T::Target>, Box<dyn Error>> {
pub async fn build_auth_client(
self,
) -> Result<AuthServiceClient<T::Target>, Box<dyn Error + Send + Sync + 'static>> {
let channel = self
.interceptor
.build_service(get_channel(&self.api_endpoint).await?);
Expand All @@ -178,7 +184,7 @@ where
#[cfg(feature = "api-management-v1")]
pub async fn build_management_client(
self,
) -> Result<ManagementServiceClient<T::Target>, Box<dyn Error>> {
) -> Result<ManagementServiceClient<T::Target>, Box<dyn Error + Send + Sync + 'static>> {
let channel = self
.interceptor
.build_service(get_channel(&self.api_endpoint).await?);
Expand All @@ -193,7 +199,9 @@ where
/// cannot be parsed into a valid URL or if the connection to the endpoint
/// is not possible.
#[cfg(feature = "api-oidc-v2")]
pub async fn build_oidc_client(self) -> Result<OidcServiceClient<T::Target>, Box<dyn Error>> {
pub async fn build_oidc_client(
self,
) -> Result<OidcServiceClient<T::Target>, Box<dyn Error + Send + Sync + 'static>> {
let channel = self
.interceptor
.build_service(get_channel(&self.api_endpoint).await?);
Expand All @@ -210,7 +218,7 @@ where
#[cfg(feature = "api-org-v2")]
pub async fn build_organization_client(
self,
) -> Result<OrganizationServiceClient<T::Target>, Box<dyn Error>> {
) -> Result<OrganizationServiceClient<T::Target>, Box<dyn Error + Send + Sync + 'static>> {
let channel = self
.interceptor
.build_service(get_channel(&self.api_endpoint).await?);
Expand All @@ -227,7 +235,7 @@ where
#[cfg(feature = "api-session-v2")]
pub async fn build_session_client(
self,
) -> Result<SessionServiceClient<T::Target>, Box<dyn Error>> {
) -> Result<SessionServiceClient<T::Target>, Box<dyn Error + Send + Sync + 'static>> {
let channel = self
.interceptor
.build_service(get_channel(&self.api_endpoint).await?);
Expand All @@ -244,7 +252,7 @@ where
#[cfg(feature = "api-settings-v2")]
pub async fn build_settings_client(
self,
) -> Result<SettingsServiceClient<T::Target>, Box<dyn Error>> {
) -> Result<SettingsServiceClient<T::Target>, Box<dyn Error + Send + Sync + 'static>> {
let channel = self
.interceptor
.build_service(get_channel(&self.api_endpoint).await?);
Expand All @@ -261,7 +269,7 @@ where
#[cfg(feature = "api-system-v1")]
pub async fn build_system_client(
self,
) -> Result<SystemServiceClient<T::Target>, Box<dyn Error>> {
) -> Result<SystemServiceClient<T::Target>, Box<dyn Error + Send + Sync + 'static>> {
let channel = self
.interceptor
.build_service(get_channel(&self.api_endpoint).await?);
Expand All @@ -276,7 +284,9 @@ where
/// cannot be parsed into a valid URL or if the connection to the endpoint
/// is not possible.
#[cfg(feature = "api-user-v2")]
pub async fn build_user_client(self) -> Result<UserServiceClient<T::Target>, Box<dyn Error>> {
pub async fn build_user_client(
self,
) -> Result<UserServiceClient<T::Target>, Box<dyn Error + Send + Sync + 'static>> {
let channel = self
.interceptor
.build_service(get_channel(&self.api_endpoint).await?);
Expand Down
4 changes: 2 additions & 2 deletions src/api/interceptors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use crate::credentials::{AuthenticationOptions, ServiceAccount};
///
/// ```
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
/// use zitadel::api::{
/// clients::ClientBuilder, zitadel::auth::v1::GetMyUserRequest,
/// };
Expand Down Expand Up @@ -99,7 +99,7 @@ impl Interceptor for AccessTokenInterceptor {
///
/// ```
/// # #[tokio::main]
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
/// use zitadel::{
/// api::{clients::ClientBuilder, zitadel::auth::v1::GetMyUserRequest},
/// credentials::{AuthenticationOptions, ServiceAccount},
Expand Down
23 changes: 14 additions & 9 deletions src/axum/introspection/state_builder.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
use custom_error::custom_error;
use std::sync::Arc;

use crate::axum::introspection::state::IntrospectionConfig;
use crate::credentials::Application;
use crate::oidc::discovery::{discover, DiscoveryError};
use crate::oidc::introspection::AuthorityAuthentication;
use std::sync::Arc;
use thiserror::Error;

#[cfg(feature = "introspection_cache")]
use crate::oidc::introspection::cache::IntrospectionCache;

use super::state::IntrospectionState;

custom_error! {
/// Error type for introspection config builder related errors.
pub IntrospectionStateBuilderError
NoAuthSchema = "no authentication for authority defined",
Discovery{source: DiscoveryError} = "could not fetch discovery document: {source}",
NoIntrospectionUrl = "discovery document did not contain an introspection url",
/// Error type for introspection config builder related errors.
#[derive(Debug, Error)]
pub enum IntrospectionStateBuilderError {
#[error("no authentication for authority defined")]
NoAuthSchema,
#[error("could not fetch discovery document: {source}")]
Discovery {
#[from]
source: DiscoveryError,
},
#[error("discovery document did not contain an introspection url")]
NoIntrospectionUrl,
}

pub struct IntrospectionStateBuilder {
Expand Down
37 changes: 23 additions & 14 deletions src/axum/introspection/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::HashMap;
use std::fmt::Debug;

use crate::axum::introspection::IntrospectionState;
use crate::oidc::introspection::{introspect, IntrospectionError, ZitadelIntrospectionResponse};
use axum::http::StatusCode;
use axum::{
extract::{FromRef, FromRequestParts},
Expand All @@ -12,22 +13,30 @@ use axum::{
use axum_extra::headers::authorization::Bearer;
use axum_extra::headers::Authorization;
use axum_extra::TypedHeader;
use custom_error::custom_error;
use openidconnect::TokenIntrospectionResponse;
use serde_json::json;

use crate::oidc::introspection::{introspect, IntrospectionError, ZitadelIntrospectionResponse};

custom_error! {
/// Error type for guard related errors.
pub IntrospectionGuardError
MissingConfig = "no introspection config given to rocket managed state",
Unauthorized = "no HTTP authorization header found",
InvalidHeader = "authorization header is invalid",
WrongScheme = "Authorization header is not a bearer token",
Introspection{source: IntrospectionError} = "introspection returned an error: {source}",
Inactive = "access token is inactive",
NoUserId = "introspection result contained no user id",
use thiserror::Error;

/// Error type for guard related errors.
#[derive(Debug, Error)]
pub enum IntrospectionGuardError {
#[error("no introspection config given to rocket managed state")]
MissingConfig,
#[error("no HTTP authorization header found")]
Unauthorized,
#[error("authorization header is invalid")]
InvalidHeader,
#[error("Authorization header is not a bearer token")]
WrongScheme,
#[error("introspection returned an error: {source}")]
Introspection {
#[from]
source: IntrospectionError,
},
#[error("access token is inactive")]
Inactive,
#[error("introspection result contained no user id")]
NoUserId,
}

impl IntoResponse for IntrospectionGuardError {
Expand Down
Loading