pub struct FeishuAuthPlugin {
app_id: String,
app_secret: String,
auth_redirect_uri: String,
code_header: String,
code_query: String,
token_url: String,
userinfo_url: String,
set_userinfo_header: bool,
timeout: Duration,
ssl_verify: bool,
resources: Arc<PluginResources>,
session: Option<FeishuSession>,
}Expand description
Authenticates requests by exchanging a Feishu authorization code for a user access token, then resolving that token to a Feishu user.
Fields§
§app_id: String§app_secret: String§auth_redirect_uri: String§code_header: String§code_query: String§token_url: String§userinfo_url: String§set_userinfo_header: bool§timeout: Duration§ssl_verify: bool§resources: Arc<PluginResources>§session: Option<FeishuSession>Session-mode settings; None keeps the stateless token-validation
behavior (the pre-existing, backward-compatible default).
Implementations§
Source§impl FeishuAuthPlugin
impl FeishuAuthPlugin
Sourcepub fn from_config(
config: &HashMap<String, Value>,
resources: &Arc<PluginResources>,
) -> Result<Self, String>
pub fn from_config( config: &HashMap<String, Value>, resources: &Arc<PluginResources>, ) -> Result<Self, String>
Builds the plugin from node config.
Accepted keys:
app_id(string, required): Feishu application id.app_secret(string, required): Feishu application secret.auth_redirect_uri(string, required): theredirect_uriregistered with Feishu; sent in theauthorization_codetoken-exchange body and must match the one used to obtain the code.code_header(string, default"X-Feishu-Code"): header the code is read from first (matched case-insensitively).code_query(string, default"code"): query parameter fallback.access_token_url(string, default Feishu’soauth/token).userinfo_url(string, default Feishu’sauthen/v1/user_info).set_userinfo_header(bool, defaulttrue): base64-encode the resolved userinfo into theX-Userinforequest header.timeout(integer ms, default6000).ssl_verify(bool, defaulttrue).
Session-mode keys (present ⇒ session mode is enabled — see the module docs):
session_secret(string) orsession.secret(string): signing/ encryption secret for the session cookie. Setting it turns on the session flow.session.cookie.name(string, default"feishu_session").session.cookie.path(string, default"/").session.cookie.lifetime(u64 seconds, default86400; APISIX’scookie_expires_in).session.storage/session.store: server-side session backend (seeserver_session::parse_backend).redirect_uri(string, required in session mode): where to 302 a browser that has neither a valid session nor a code. Distinct fromauth_redirect_uri, which stays required always.
secret_fallbacks (APISIX multi-secret rotation) is not accepted —
see the module docs.
type: feishu-auth
config:
app_id: ${FEISHU_APP_ID}
app_secret: ${FEISHU_APP_SECRET}
auth_redirect_uri: https://app.example.com/callback
session:
secret: ${FEISHU_SESSION_SECRET}
redirect_uri: https://login.example.com/startfn extract_code(&self, ctx: &Context) -> Option<String>
Sourceasync fn fetch_access_token(
&self,
code: &str,
) -> Result<(String, Option<u64>), FeishuError>
async fn fetch_access_token( &self, code: &str, ) -> Result<(String, Option<u64>), FeishuError>
Exchanges code for a Feishu user access token, returning the token
and (when present) its expires_in seconds.
Sourcefn token_request_body(&self, code: &str) -> Value
fn token_request_body(&self, code: &str) -> Value
Builds the authorization_code token-exchange body.
Sourceasync fn fetch_userinfo(&self, access_token: &str) -> Result<Value, FeishuError>
async fn fetch_userinfo(&self, access_token: &str) -> Result<Value, FeishuError>
Resolves the access token to Feishu userinfo.
Sourcefn reject(
ctx: Context,
message: &str,
) -> Result<PluginOutput, PluginExecutionError>
fn reject( ctx: Context, message: &str, ) -> Result<PluginOutput, PluginExecutionError>
Builds the 401 rejection and exits on the denied port. Reserved
for a deliberate denial — a missing code, or Feishu actively
rejecting the code/token.
Sourcefn upstream_error(
ctx: Context,
message: &str,
) -> Result<PluginOutput, PluginExecutionError>
fn upstream_error( ctx: Context, message: &str, ) -> Result<PluginOutput, PluginExecutionError>
Builds a genuine infrastructure-failure Err for a Feishu callout
that failed outright (network error, non-200, unparseable body) —
unlike reject, the node could not do its job rather than Feishu
deliberately refusing the code.
Sourcefn store_error(ctx: Context, e: StoreError) -> PluginExecutionError
fn store_error(ctx: Context, e: StoreError) -> PluginExecutionError
Session-store outage: 503 through the error port. Deliberately NOT 401 — a store outage is not “unauthenticated”.
Sourcefn redirect(
ctx: Context,
location: &str,
set_cookies: Vec<String>,
) -> Result<PluginOutput, PluginExecutionError>
fn redirect( ctx: Context, location: &str, set_cookies: Vec<String>, ) -> Result<PluginOutput, PluginExecutionError>
Builds a 302 early-exit carrying the prepared response, and exits on
the redirect port. Wire the node’s redirect edge to client.in so
this reaches the browser.
Sourcefn session_attrs<'a>(
session: &'a FeishuSession,
ctx: &Context,
) -> CookieAttrs<'a>
fn session_attrs<'a>( session: &'a FeishuSession, ctx: &Context, ) -> CookieAttrs<'a>
Cookie attributes for the session cookie: HttpOnly, SameSite=Lax,
and Secure only over HTTPS (so plain-HTTP dev works).
Sourceasync fn read_session(
&self,
ctx: &Context,
session: &FeishuSession,
) -> Result<(Option<FeishuSessionData>, Option<String>), StoreError>
async fn read_session( &self, ctx: &Context, session: &FeishuSession, ) -> Result<(Option<FeishuSessionData>, Option<String>), StoreError>
Reads the session cookie via the configured backend.
Ok(Some(data)) = a valid session; Ok(None) = no session (no
cookie, unopenable/expired/tampered value, or a payload that failed to
decode as JSON — in which case the session was also destroyed so a
stale entry does not linger); Err = store outage (503 via
FeishuAuthPlugin::store_error), never a silent re-login.
When the payload was undecodable, the returned delete-cookie
Set-Cookie value is included so the caller can forward it on
whatever response it ultimately builds.
Sourceasync fn execute_session(
&self,
ctx: Context,
session: &FeishuSession,
) -> Result<PluginOutput, PluginExecutionError>
async fn execute_session( &self, ctx: Context, session: &FeishuSession, ) -> Result<PluginOutput, PluginExecutionError>
Session-mode flow: read session → callback (code) → begin login.
Trait Implementations§
Source§impl Plugin for FeishuAuthPlugin
impl Plugin for FeishuAuthPlugin
Source§fn plugin_type(&self) -> &str
fn plugin_type(&self) -> &str
Source§fn execute<'life0, 'async_trait>(
&'life0 self,
ctx: Context,
) -> Pin<Box<dyn Future<Output = Result<PluginOutput, PluginExecutionError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn execute<'life0, 'async_trait>(
&'life0 self,
ctx: Context,
) -> Pin<Box<dyn Future<Output = Result<PluginOutput, PluginExecutionError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn reads_response_body(&self) -> bool
fn reads_response_body(&self) -> bool
context.response.body. Read moreSource§fn cache_target(&self) -> Option<CacheTarget>
fn cache_target(&self) -> Option<CacheTarget>
proxy-cache half. Read moreAuto Trait Implementations§
impl !RefUnwindSafe for FeishuAuthPlugin
impl !UnwindSafe for FeishuAuthPlugin
impl Freeze for FeishuAuthPlugin
impl Send for FeishuAuthPlugin
impl Sync for FeishuAuthPlugin
impl Unpin for FeishuAuthPlugin
impl UnsafeUnpin for FeishuAuthPlugin
Blanket Implementations§
§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more