openlark-auth 0.15.0

OpenLark 认证模块 - 提供令牌管理、缓存、验证和刷新功能
Documentation

OpenLark 认证服务模块

🏗️ 相对路径: crates/openlark-auth/ 📦 Crate类型: 认证服务核心模块 🎯 职责: 提供飞书开放平台的完整认证服务 📊 覆盖率: 🟢 80%

模块职责

openlark-auth 是 OpenLark SDK 的认证服务核心模块,基于 Project-Version-Resource (PVR) 架构设计,提供飞书开放平台的完整认证功能:

  • 企业应用认证: 自建应用和商店应用的认证管理
  • 用户身份认证: 用户访问令牌、信息验证和 OIDC 认证
  • OAuth 授权: 标准化的 OAuth 授权流程
  • 令牌管理: 自动化的令牌生命周期管理
  • 类型安全: 完全的类型安全和编译时检查

架构设计

PVR 三层架构

graph TD
    A[openlark-auth] --> B[models/]
    A --> C[auth/]
    A --> D[authen/]
    A --> E[oauth/]

    B --> B1[AuthConfig]
    B --> B2[AuthError]
    B --> B3[TokenInfo]
    B --> B4[UserInfo]

    C --> C1[Project: auth]
    C1 --> C11[Version: v3]
    C11 --> C111[Resource: tenant_access_token]
    C11 --> C112[Resource: app_access_token]
    C11 --> C113[Resource: app_ticket]

    D --> D1[Project: authen]
    D1 --> D11[Version: v1]
    D11 --> D111[Resource: user_info]
    D11 --> D112[Resource: oidc]
    D11 --> D113[Resource: access_token]

    E --> E1[Project: oauth]
    E1 --> E11[Version: old]
    E11 --> E111[Resource: authorization]

核心组件

// 认证服务统一入口
use openlark_auth::{AuthServices, AuthConfig};

let config = AuthConfig::new("app_id", "app_secret")
.with_base_url("https://open.feishu.cn");

let auth = AuthServices::new(config);

// 访问各个项目
let tenant_token = auth.auth.v3().tenant_access_token().internal().send().await?;
let user_info = auth.authen.v1.user_info().get().user_access_token("token").send().await?;
let oauth_url = auth.oauth.old.authorization().get_index().app_id("app_id").send().await?;

快速开始

安装依赖

[dependencies]
openlark-auth = { version = "0.1.0-dev" }
tokio = { version = "1.0", features = ["full"] }

基础使用

use openlark_auth::{AuthServices, AuthConfig};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. 创建认证配置
    let config = AuthConfig::new("your_app_id", "your_app_secret")
        .with_base_url("https://open.feishu.cn");

    // 2. 创建认证服务
    let auth = AuthServices::new(config);

    // 3. 获取自建应用租户访问令牌
    let tenant_token = auth.auth.v3().tenant_access_token()
        .internal()
        .send()
        .await?;

    println!("租户令牌: {}", tenant_token.tenant_access_token);

    // 4. 获取用户信息
    let user_info = auth.authen.v1().user_info()
        .get()
        .user_access_token(&tenant_token.tenant_access_token)
        .send()
        .await?;

    println!("用户名称: {}", user_info.name);

    Ok(())
}

API 详细说明

企业应用认证 (auth/v3)

租户访问令牌

// 自建应用租户访问令牌
let tenant_token = auth.auth.v3().tenant_access_token()
    .internal()
    .send()
    .await?;

// 商店应用租户访问令牌
let tenant_token = auth.auth.v3().tenant_access_token()
    .store()
    .app_access_token("app_token")
    .tenant_key("tenant_key")
    .send()
    .await?;

应用访问令牌

// 自建应用访问令牌
let app_token = auth.auth.v3().app_access_token()
    .internal()
    .send()
    .await?;

// 商店应用访问令牌
let app_token = auth.auth.v3().app_access_token()
    .store()
    .send()
    .await?;

应用票据管理

// 重新推送应用票据
let response = auth.auth.v3().app_ticket()
    .resend()
    .send()
    .await?;

用户身份认证 (authen/v1)

用户信息获取

let user_info = auth.authen.v1().user_info()
    .get()
    .user_access_token("user_access_token")
    .user_id_type("open_id")
    .send()
    .await?;

用户访问令牌

// 使用授权码获取访问令牌
let access_token = auth.authen.v1().access_token()
    .create()
    .grant_type("authorization_code")
    .code("authorization_code")
    .send()
    .await?;

OIDC 认证

// 刷新 OIDC 访问令牌
let oidc_token = auth.authen.v1().oidc()
    .create_refresh_access_token()
    .refresh_token("refresh_token")
    .grant_type("refresh_token")
    .send()
    .await?;

OAuth 授权 (oauth/old)

获取预授权码

let pre_auth_code = auth.oauth.old.authorization()
    .get_index()
    .app_id("app_id")
    .redirect_uri("https://example.com/callback")
    .scope("user:info")
    .state("random_state")
    .send()
    .await?;

数据模型

认证配置 (AuthConfig)

let config = AuthConfig::new("app_id", "app_secret")
    .with_base_url("https://open.feishu.cn");

// 或者使用默认配置
let default_config = AuthConfig::default();

令牌信息 (TokenInfo)

let token_info = TokenInfo {
    access_token: "your_access_token".to_string(),
    token_type: "Bearer".to_string(),
    expires_in: 7200,
    expires_at: chrono::Utc::now() + chrono::Duration::hours(2),
    scope: Some("user:info docs:read".to_string()),
};

// 令牌状态检查
assert!(!token_info.is_expired());
assert!(token_info.remaining_seconds() > 3600);
assert!(!token_info.needs_refresh(30)); // 30分钟内不需要刷新

用户信息 (UserInfo)

// 响应字段包括:
// 用户身份信息
// - open_id: 用户Open ID(应用级别)
// - union_id: 用户Union ID(跨应用)
// - user_id: 用户ID(系统内部)
//
// 基本信息
// - name: 用户姓名
// - en_name: 用户英文名/昵称
//
// 联系方式
// - email: 用户个人邮箱地址
// - enterprise_email: 用户企业邮箱地址
// - mobile: 用户手机号码
//
// 头像信息
// - avatar_url: 用户头像URL(单一链接)
// - avatar: 用户头像对象(多尺寸)
//   - avatar_72: 72x72像素头像
//   - avatar_240: 240x240像素头像
//   - avatar_640: 640x640像素头像
//   - avatar_origin: 原始尺寸头像
//
// 组织信息
// - tenant_key: 租户密钥
// - employee_no: 员工工号
// - department_ids: 用户所属部门ID列表
// - group_ids: 用户所属群组ID列表
// - positions: 用户职位信息列表
//
// 状态信息
// - status: 用户状态对象
//   - is_activated: 是否已激活
//   - is_joined: 是否已加入企业
//   - is_reserved: 是否为预留用户
//   - is_exited: 是否已离职
//
// 扩展信息
// - dingtalk_user_id: 钉钉用户ID
// - enterprise_extension: 企业扩展属性对象
// - custom_attrs: 用户自定义属性列表

错误处理

错误类型

use openlark_auth::AuthError;

match result {
    Ok(response) => println!("成功: {:?}", response),
    Err(AuthError::ConfigError(msg)) => {
        println!("配置错误: {}", msg);
    }
    Err(AuthError::NetworkError(e)) => {
        println!("网络错误: {}", e);
    }
    Err(AuthError::APIError { code, message }) => {
        println!("API错误 [{}]: {}", code, message);
    }
}

错误恢复策略

// 令牌过期自动刷新
if token_info.is_expired() || token_info.needs_refresh(30) {
    let new_token = refresh_token(&refresh_token).await?;
    // 使用新令牌重试请求
}

// 网络错误重试
let result = retry_async_operation(|| {
    auth.auth.v3().tenant_access_token().internal().send()
}).await;

高级功能

并发处理

use std::sync::Arc;
use tokio::task::JoinSet;

let auth = Arc::new(AuthServices::new(config));
let mut join_set = JoinSet::new();

// 并发获取多个令牌
for i in 0..5 {
let auth_clone = auth.clone();
join_set.spawn(async move {
auth_clone.auth.v3().tenant_access_token().internal().send().await
});
}

// 等待所有请求完成
while let Some(result) = join_set.join_next().await {
match result {
Ok(token) => println ! ("令牌 {} 获取成功", i),
Err(e) => println !("令牌 {} 获取失败: {}", i, e),
}
}

自定义配置

// 自定义 HTTP 客户端
use reqwest::Client;

let client = Client::builder()
    .timeout(Duration::from_secs(30))
    .user_agent("OpenLark-SDK/1.0")
    .build()?;

let config = AuthConfig::new("app_id", "app_secret")
    .with_base_url("https://open.feishu.cn")
    .with_client(client);

最佳实践

1. 配置管理

// 使用环境变量
use std::env;

let app_id = env::var("LARK_APP_ID")
    .expect("LARK_APP_ID must be set");
let app_secret = env::var("LARK_APP_SECRET")
    .expect("LARK_APP_SECRET must be set");

let config = AuthConfig::new(app_id, app_secret);

2. 令牌缓存

// 使用内存缓存存储令牌
use std::collections::HashMap;
use tokio::sync::RwLock;

struct TokenCache {
    inner: RwLock<HashMap<String, TokenInfo>>,
}

impl TokenCache {
    async fn get(&self, key: &str) -> Option<TokenInfo> {
        self.inner.read().await.get(key).cloned()
    }

    async fn set(&self, key: String, token: TokenInfo) {
        self.inner.write().await.insert(key, token);
    }
}

3. 自动刷新机制

async fn get_valid_token<T, Fut, F>(
    cache: &TokenCache,
    key: &str,
    refresh_fn: F,
) -> Result<String, AuthError>
where
    T: Into<String>,
    F: FnOnce() -> Fut,
    Fut: Future<Output = Result<TokenInfo, AuthError>>,
{
    // 检查缓存
    if let Some(token) = cache.get(key).await {
        if !token.is_expired() {
            return Ok(token.access_token);
        }
    }

    // 刷新令牌
    let new_token = refresh_fn().await?;
    cache.set(key.to_string(), new_token.clone()).await;

    Ok(new_token.access_token)
}

功能标志

[dependencies]
openlark-auth = { version = "0.1.0-dev", features = [
    "token-management",  # 令牌管理功能
    "cache",            # 缓存功能
    "oauth",            # OAuth 功能
    "encryption",      # 加密功能
] }
  • token-management: 基础令牌管理功能
  • cache: 内存缓存支持
  • oauth: OAuth 授权功能
  • encryption: 令牌加密存储

测试

运行测试

# 运行所有测试
cargo test -p openlark-auth

# 运行集成测试
cargo test -p openlark-auth --test integration_tests

# 运行 Mock 测试
cargo test -p openlark-auth --test mock_tests

# 运行特定测试
cargo test -p openlark-auth auth_v3

测试覆盖率

# 安装 cargo-llvm-cov
cargo install cargo-llvm-cov

# 生成覆盖率报告
cargo llvm-cov --lib -p openlark-auth

# 生成 HTML 报告
cargo llvm-cov --lib -p openlark-auth --html

构建状态

  • ✅ 编译状态: 零警告编译
  • ✅ 测试状态: 23/23 测试通过
  • ✅ 文档状态: 100% 中文文档覆盖
  • ✅ API 稳定性: 稳定的公共 API

版本历史

  • v0.1.0-dev (当前版本)
    • ✅ 完成 PVR 架构重构
    • ✅ 实现 9 个认证 API
    • ✅ 完整测试覆盖
    • ✅ 企业级代码质量

贡献指南

开发环境设置

# 克隆项目
git clone https://github.com/foxzool/openlark.git
cd openlark

# 进入模块目录
cd crates/openlark-auth

# 安装开发依赖
cargo build

# 运行测试
cargo test

# 检查代码格式
cargo fmt --check

# 运行 linter
cargo clippy -- -D warnings

添加新 API

  1. 在相应的 src/{project}/{version}/ 目录下创建新的资源文件
  2. 实现构建器模式 API
  3. 添加相应的数据模型到 src/models/
  4. lib.rs 中导出新服务
  5. 编写单元测试和集成测试
  6. 更新文档

许可证

本项目采用 Apache License 2.0 许可证。

联系方式