使用Rust openidconnect crate自定义Claims遇trait绑定错误求助
问题
在使用openidconnect crate对接Zitadel实例时,基础认证流程正常,但需要从Zitadel获取额外Claims。现有openidconnect::CoreClient定义如下:
pub type CoreClient = Client< EmptyAdditionalClaims, CoreAuthDisplay, CoreGenderClaim, CoreJweContentEncryptionAlgorithm, CoreJwsSigningAlgorithm, CoreJsonWebKeyType, CoreJsonWebKeyUse, CoreJsonWebKey, CoreAuthPrompt, StandardErrorResponse<CoreErrorResponseType>, CoreTokenResponse, CoreTokenType, CoreTokenIntrospectionResponse, CoreRevocableToken, CoreRevocationErrorResponse, >;
尝试将其中的EmptyAdditionalClaims替换为自定义的MyClaims:
#[derive(Serialize, Deserialize, Debug)] pub struct MyClaims { // 自定义额外Claims字段,比如zitadel的用户属性 } impl AdditionalClaims for MyClaims {}
然后复制CoreClient定义并替换第一个参数创建MyClient:
pub type MyClient = Client< MyClaims, // 复制上述其他参数 // ... >;
最终出现以下编译错误:
error[E0277]: the trait bound `StandardTokenResponse<IdTokenFields<EmptyAdditionalClaims, EmptyExtraTokenFields, CoreGenderClaim, CoreJweContentEncryptionAlgorithm, CoreJwsSigningAlgorithm, CoreJsonWebKeyType>, BasicTokenType>: openidconnect::TokenResponse<MyClaims, CoreGenderClaim, CoreJweContentEncryptionAlgorithm, CoreJwsSigningAlgorithm, CoreJsonWebKeyType, BasicTokenType>` is not satisfied --> app/src/auth.rs:92:17 | 92 | pub client: MyClient, | ^^^^^^^^ the trait `openidconnect::TokenResponse<MyClaims, CoreGenderClaim, CoreJweContentEncryptionAlgorithm, CoreJwsSigningAlgorithm, CoreJsonWebKeyType, BasicTokenType>` is not implemented for `StandardTokenResponse<IdTokenFields<EmptyAdditionalClaims, EmptyExtraTokenFields, CoreGenderClaim, CoreJweContentEncryptionAlgorithm, CoreJwsSigningAlgorithm, CoreJsonWebKeyType>, BasicTokenType>` | = help: the trait `openidconnect::TokenResponse<AC, GC, JE, JS, JT, TT>` is implemented for `StandardTokenResponse<IdTokenFields<AC, EF, GC, JE, JS, JT>, TT>` note: required by a bound in `openidconnect::Client` --> /home/domma/development/axum-zitadell-demo/openidconnect-rs/src/lib.rs:794:9 | 794 | TR: TokenResponse<AC, GC, JE, JS, JT, TT>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Client`
解决方案
错误核心是CoreTokenResponse本质是StandardTokenResponse<IdTokenFields<EmptyAdditionalClaims, ...>, ...>,它的泛型参数仍使用EmptyAdditionalClaims,和自定义的MyClaims不匹配。需要同时替换TokenResponse中的IdTokenFields泛型参数,而非仅替换Client的第一个参数。
完整实现示例
1. 定义自定义Claims
use serde::{Deserialize, Serialize}; use openidconnect::AdditionalClaims; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct MyClaims { // 匹配Zitadel返回的实际额外字段,示例为常见字段 pub username: String, pub email_verified: bool, pub name: String, pub preferred_username: String, } impl AdditionalClaims for MyClaims {}
2. 定义自定义IdTokenFields和TokenResponse
use openidconnect::{ CoreGenderClaim, CoreJweContentEncryptionAlgorithm, CoreJwsSigningAlgorithm, CoreJsonWebKeyType, BasicTokenType, IdTokenFields, StandardTokenResponse, EmptyExtraTokenFields }; // 替换IdTokenFields中的额外Claims类型为MyClaims pub type MyIdTokenFields = IdTokenFields< MyClaims, EmptyExtraTokenFields, CoreGenderClaim, CoreJweContentEncryptionAlgorithm, CoreJwsSigningAlgorithm, CoreJsonWebKeyType, >; // 基于自定义IdTokenFields创建TokenResponse pub type MyTokenResponse = StandardTokenResponse<MyIdTokenFields, BasicTokenType>;
3. 定义自定义Client类型
use openidconnect::Client; pub type MyClient = Client< MyClaims, openidconnect::CoreAuthDisplay, CoreGenderClaim, CoreJweContentEncryptionAlgorithm, CoreJwsSigningAlgorithm, CoreJsonWebKeyType, openidconnect::CoreJsonWebKeyUse, openidconnect::CoreJsonWebKey, openidconnect::CoreAuthPrompt, openidconnect::StandardErrorResponse<openidconnect::CoreErrorResponseType>, MyTokenResponse, // 此处使用自定义的TokenResponse openidconnect::CoreTokenType, openidconnect::CoreTokenIntrospectionResponse, openidconnect::CoreRevocableToken, openidconnect::CoreRevocationErrorResponse, >;
4. 使用自定义Client获取额外Claims
use openidconnect::{AuthorizationCode, ClientId, ClientSecret, RedirectUrl, Scope, IssuerUrl}; async fn create_my_client() -> Result<MyClient, openidconnect::Error<openidconnect::StandardErrorResponse<openidconnect::CoreErrorResponseType>>> { let issuer = IssuerUrl::new("https://your-zitadel-instance.com".to_string())?; let client_id = ClientId::new("your-client-id".to_string()); let client_secret = ClientSecret::new("your-client-secret".to_string()); let redirect_url = RedirectUrl::new("http://localhost:3000/callback".to_string())?; MyClient::discover(issuer) .await? .set_client_id(client_id) .set_client_secret(client_secret) .set_redirect_uri(redirect_url) .build() } async fn handle_callback(code: AuthorizationCode, client: MyClient) -> Result<(), openidconnect::Error<openidconnect::StandardErrorResponse<openidconnect::CoreErrorResponseType>>> { let token_response = client.exchange_code(code).await?; // 提取自定义Claims let id_token = token_response.id_token().ok_or(openidconnect::Error::MissingIdToken)?; let my_claims = id_token.additional_claims(); println!("用户名: {}", my_claims.username); println!("邮箱验证状态: {}", my_claims.email_verified); Ok(()) }
关键说明
Client的泛型参数中,第11个参数TR(TokenResponse)必须与第一个参数AC(AdditionalClaims)匹配,即TokenResponse内部的IdTokenFields必须使用相同的AC类型。MyClaims的字段名和类型需与Zitadel实际返回的额外Claims完全一致,可在Zitadel控制台的用户属性或应用配置中确认。
内容的提问来源于stack exchange,提问作者Achim
相关产品推荐
相关产品推荐

