一个使用 Fastify + TypeScript + oidc-provider 实现的可扩展 OIDC (OpenID Connect) 身份提供者,支持多种认证方式的插件化架构。
- ✅ 完整的 OIDC 认证流程支持
- ✅ 插件化认证架构
- ✅ 多种认证方式(本地密码、飞书、可扩展)
- ✅ 统一登录页面
- ✅ OAuth State 管理(防 CSRF)
- ✅ 用户仓储抽象层
- ✅ 动态路由和静态资源
- ✅ Webhook 支持
- ✅ TypeScript 类型安全
- ✅ 完整的测试覆盖
- 🔐 本地密码认证 - 支持 htpasswd 格式(bcrypt, MD5, SHA)
- 🚀 飞书认证 - 完整的 OAuth 2.0 流程
- 🔌 可扩展 - 轻松添加新的认证方式
pnpm install# 复制示例配置(支持 .json 或 .js 格式,.js 优先级更高)
cp example.gitea-oidc.config.json gitea-oidc.config.json
# 或使用 .js 格式以支持动态配置
# cp example.gitea-oidc.config.json gitea-oidc.config.js
# 创建密码文件(本地认证)
node -e "const bcrypt = require('bcrypt'); console.log('admin:' + bcrypt.hashSync('admin123', 10));" > .htpasswd# 开发模式(热重载)
pnpm dev
# 生产模式
pnpm build && pnpm start服务器将在 http://localhost:3000 启动
如果你需要在其他项目中集成此服务器,可以作为模块导入:
import { start } from 'gitea-oidc/server';
import type { GiteaOidcConfig } from 'gitea-oidc/config';
// 使用自定义配置启动
const customConfig: GiteaOidcConfig = {
server: {
host: '0.0.0.0',
port: 4000,
url: 'http://localhost:4000',
trustProxy: false,
},
// ... 其他配置
};
const app = await start(customConfig);
// 或者不传入配置,使用配置文件
const app = await start();详细示例请参考 example-import-usage.ts 文件。
# 运行测试
pnpm test
# 查看覆盖率
pnpm test:coverage- 快速开始 - 5 分钟快速上手
- 生产环境配置 - ⭐ 生产环境部署指南
- 集成完成 - 集成状态和使用说明
- 实现状态 & 验证清单 - 核心功能实现与验证状态
- 设计文档 - 架构设计详解
- 插件开发 - 如何开发自定义插件
- P0 改进 - 安全性改进说明
- 集成指南 - 详细集成步骤
- OIDC 帮助 - OIDC 相关说明
- 发布与 CI/CD 指南 - release-it 与 GitHub Actions 工作流说明
- Node.js 22+ - JavaScript 运行时环境
- Fastify 5.x - 高性能 Node.js Web 框架
- oidc-provider 9.x - OpenID Certified™ OIDC 服务器
- TypeScript 5.x - 类型安全
- Vitest - 测试框架
- Rolldown - 高性能打包工具
- bcrypt - 密码哈希
- better-sqlite3 - SQLite 数据库
- pg - PostgreSQL 客户端
- redis - Redis 客户端
- Zod - 配置验证
gitea-oidc/
├── src/
│ ├── server.ts # Fastify + oidc-provider 入口
│ ├── config.ts # 配置加载与合并
│ ├── core/ # 认证核心
│ │ ├── AuthCoordinator.ts
│ │ └── PermissionChecker.ts
│ ├── adapters/ # OIDC 持久化适配器
│ │ ├── OidcAdapterFactory.ts
│ │ ├── SqliteOidcAdapter.ts
│ │ └── RedisOidcAdapter.ts
│ ├── stores/ # OAuth State / 认证结果存储
│ │ └── MemoryStateStore.ts
│ ├── providers/ # 认证提供者插件
│ │ ├── LocalAuthProvider.ts
│ │ └── FeishuAuthProvider.ts
│ ├── repositories/ # 用户仓储
│ │ ├── MemoryUserRepository.ts
│ │ ├── SqliteUserRepository.ts
│ │ └── PgsqlUserRepository.ts
│ ├── schemas/ # Zod 配置 Schema
│ ├── types/ # 类型定义
│ ├── utils/ # 工具与日志
│ └── __tests__/ # 单元测试
├── docs/ # 文档
├── public/ # 静态页面(首页、会话过期页等)
├── example.gitea-oidc.config.json # JSON 配置示例
├── gitea-oidc.config.js # JS 配置示例(支持动态配置)
├── Dockerfile # Docker 镜像构建
├── pnpm-lock.yaml
└── vitest.config.ts # Vitest 配置支持两种配置文件格式(按优先级排序):
- gitea-oidc.config.js - JavaScript 格式,支持动态配置、环境变量、函数导出
- gitea-oidc.config.json - JSON 格式,静态配置
{
"server": {
"host": "0.0.0.0",
"port": 3000,
"url": "http://localhost:3000",
"trustProxy": false
},
"logging": {
"enabled": true,
"level": "info"
},
"oidc": {
"issuer": "http://localhost:3000",
"cookieKeys": [
"change-this-to-a-random-string-in-production",
"and-another-one-for-key-rotation"
],
"ttl": {
"AccessToken": 3600,
"AuthorizationCode": 600,
"IdToken": 3600,
"RefreshToken": 86400
},
"claims": {
"openid": ["sub"],
"profile": ["name", "email", "email_verified", "picture"]
},
"features": {
"devInteractions": { "enabled": false },
"registration": { "enabled": false },
"revocation": { "enabled": true }
}
},
"clients": [
{
"client_id": "gitea",
"client_secret": "gitea-client-secret-change-in-production",
"redirect_uris": ["http://localhost:3001/user/oauth2/gitea/callback"],
"response_types": ["code"],
"grant_types": ["authorization_code", "refresh_token"],
"token_endpoint_auth_method": "client_secret_basic"
}
],
"auth": {
"userRepository": {
"type": "memory",
"memory": {}
},
"providers": {
"local": {
"enabled": true,
"displayName": "本地密码",
"priority": 1,
"config": {
"passwordFile": ".htpasswd",
"passwordFormat": "bcrypt"
}
},
"feishu": {
"enabled": false,
"displayName": "飞书登录",
"priority": 2,
"config": {
"appId": "cli_your_app_id_here",
"appSecret": "your_app_secret_here",
"redirectUri": "http://localhost:3000/auth/feishu/callback",
"scope": "contact:user.base:readonly",
"autoCreateUser": true,
"userMapping": {
"username": "en_name",
"name": "name",
"email": "email"
},
"encryptKey": "your_encrypt_key_here",
"verificationToken": "your_verification_token_here"
}
}
}
},
"adapter": {
"type": "sqlite",
"sqlite": {
"dbPath": "./oidc.db"
}
}
}host: 服务器监听地址(0.0.0.0表示监听所有网络接口)port: 服务器端口url: 公开访问的完整 URLtrustProxy: 是否信任反向代理(Nginx/Traefik 后必须启用)
enabled: 是否启用日志level: 日志级别(info|warn|error|debug)
issuer: OIDC 发行者 URL,必须与对外访问的 OIDC 根路径一致(例如https://auth.example.com/oidc),应与实际挂载路径/oidc对应cookieKeys: Cookie 签名密钥数组,支持密钥轮换ttl: 各种令牌的生存时间(秒)claims: OIDC 声明配置features: 功能开关
支持三种用户仓储类型:
{
"type": "memory",
"memory": {}
}{
"type": "sqlite",
"sqlite": {
"dbPath": "./users.db"
}
}{
"type": "pgsql",
"pgsql": {
"connectionString": "postgresql://user:pass@localhost:5432/dbname"
}
}或使用分离的配置:
{
"type": "pgsql",
"pgsql": {
"host": "localhost",
"port": 5432,
"database": "gitea_oidc",
"user": "postgres",
"password": "password"
}
}OIDC 数据持久化适配器配置,支持三种类型:
{
"type": "sqlite",
"sqlite": {
"dbPath": "./oidc.db"
}
}{
"type": "redis",
"redis": {
"host": "localhost",
"port": 6379,
"password": "optional",
"db": 0
}
}{
"type": "memory"
}.htpasswd 文件中的测试用户:
- 用户名:
admin/ 密码:admin123 - 用户名:
testuser/ 密码:password
- 进入 Gitea 管理面板 → 认证源 → 添加认证源
- 选择 OpenID Connect
- 填写配置:
- 发现 URL:
http://localhost:3000/oidc/.well-known/openid-configuration - 客户端 ID:
gitea - 客户端密钥:
gitea-client-secret-change-in-production
- 发现 URL:
- 保存配置
- 访问 Gitea 登录页面
- 点击 OIDC 登录按钮
- 使用测试账户登录(admin/admin123)
- 成功后自动返回 Gitea
项目提供官方 Docker 镜像,可用于快速部署。
# 拉取最新版本
docker pull lydamirror/gitea-oidc:latest
# 拉取指定版本
docker pull lydamirror/gitea-oidc:<version> # 例如 1.0.22,具体以实际发布的 tag 为准# 基本运行
docker run -d -p 3000:3000 lydamirror/gitea-oidc
# 使用自定义配置(JSON 格式)
docker run -p 3000:3000 \
-e NODE_ENV=production \
-v ./gitea-oidc.config.json:/app/gitea-oidc.config.json \
--log-opt max-size=10m \
--log-opt max-file=3 \
lydamirror/gitea-oidc
# 使用自定义配置(JS 格式)
docker run -p 3000:3000 \
-e NODE_ENV=production \
-v ./gitea-oidc.config.js:/app/gitea-oidc.config.js \
--log-opt max-size=10m \
--log-opt max-file=3 \
lydamirror/gitea-oidcNODE_ENV: 运行环境(默认 development)PORT: 当前服务不会自动读取该变量,请通过配置文件server.port和容器端口映射控制实际监听端口
# 使用 SQLite 持久化(OIDC 会话数据)
docker run -d -p 3000:3000 \
-v /host/data:/app/data \
-v ./gitea-oidc.config.json:/app/gitea-oidc.config.json \
lydamirror/gitea-oidc
# 配置文件中设置:
# "adapter": {
# "type": "sqlite",
# "sqlite": {
# "dbPath": "/app/data/oidc.db"
# }
# }
# 使用 Redis 持久化(分布式部署)
docker run -d -p 3000:3000 \
-v ./gitea-oidc.config.json:/app/gitea-oidc.config.json \
lydamirror/gitea-oidc
# 配置文件中设置:
# "adapter": {
# "type": "redis",
# "redis": {
# "host": "redis",
# "port": 6379
# }
# }- 更换 Cookie 密钥:修改配置中的
oidc.cookieKeys - 更换客户端密钥:修改
clients[].client_secret - 使用 HTTPS:配置 SSL 证书,更新
server.url为 https - 启用反向代理支持:设置
server.trustProxy: true - 强密码策略:使用 bcrypt 生成强密码(
passwordFormat: "bcrypt") - 持久化存储:
- 用户数据:使用 PostgreSQL 或 SQLite(
auth.userRepository.type) - OIDC 会话:使用 Redis 或 SQLite(
adapter.type)
- 用户数据:使用 PostgreSQL 或 SQLite(
- 日志管理:配置适当的日志级别(
logging.level) - 限制访问:配置防火墙规则,仅允许必要的端口访问
{
"server": {
"host": "0.0.0.0",
"port": 3000,
"url": "https://auth.example.com",
"trustProxy": true
},
"logging": {
"enabled": true,
"level": "warn"
},
"oidc": {
"issuer": "https://auth.example.com/oidc",
"cookieKeys": ["your-secure-random-key-1", "your-secure-random-key-2"]
},
"auth": {
"userRepository": {
"type": "pgsql",
"pgsql": {
"connectionString": "postgresql://user:pass@db:5432/auth"
}
}
},
"adapter": {
"type": "redis",
"redis": {
"host": "redis",
"port": 6379
}
}
}已实现功能:
- ✅ 数据库用户仓储(PostgreSQL、SQLite)
- ✅ Redis 适配器(分布式部署)
- ✅ 插件化认证架构
- ✅ Webhook 支持
计划中的功能:
- ⏳ 添加更多认证插件(企业微信、钉钉、LDAP)
- ⏳ 添加管理界面
- ⏳ 实现 MFA 支持
- ⏳ 用户自助服务(密码重置、账号管理)
更多关于发布与 CI/CD 的说明已迁移至独立文档:
- 发布与 CI/CD 指南 - release-it 与 GitHub Actions 工作流说明
该文档涵盖:
- 使用
release-it进行版本发布与 npm 发布 - 发布所需环境变量 / GitHub Secrets 配置
- GitHub Actions 中
Release/CI-CHECK工作流的执行流程 - Docker 镜像构建与推送流程
本项目使用 GitHub Actions 提供完整的 CI 与发布自动化,详情请参考上面的「发布与 CI/CD 指南」文档。
MIT License
XGJ lydanne
更多详细信息请查看 文档目录