feat: sync HTTP callback OIDC setup

This commit is contained in:
A 2026-07-21 18:18:33 +08:00
parent bf72c246b6
commit f53579416e
26 changed files with 557 additions and 133 deletions

View file

@ -56,10 +56,10 @@ python .\cmd\bots\aiogramecho\echo.py `
## Webhook 模式
telesrv 现在会持久化 webhook 配置,通过跨实例租约投递,并且只在目标返回 2xx
后推进 `update_id`。aiogram 可监听本机 HTTP由 Caddy/Nginx/Tunnel 提供公网 HTTPS
后推进 `update_id`。aiogram 可直接登记 HTTP/HTTPS 域名或 IP也可以由 Caddy/Nginx/Tunnel 提供公网 HTTPS
```powershell
$env:TELESRV_BOT_WEBHOOK_URL = "https://bot.example.com/webhook"
$env:TELESRV_BOT_WEBHOOK_URL = "http://192.0.2.25:8080/webhook"
$env:TELESRV_BOT_WEBHOOK_SECRET = "replace_with_a_random_secret"
python .\cmd\bots\aiogramecho\echo.py `
--mode webhook `
@ -69,8 +69,8 @@ python .\cmd\bots\aiogramecho\echo.py `
--drop-pending
```
公网 URL 必须是 HTTPS端口限 Telegram 标准的 443/80/88/8443;本机监听地址
可以是 HTTP因为 TLS 通常在反向代理终止`secret_token` 会由 telesrv 放入
Webhook URL 可使用任意合法 HTTP/HTTPS 域名或 IP 及 `1..65535` 端口;本机监听地址
也可以直接使用 HTTP`secret_token` 会由 telesrv 放入
`X-Telegram-Bot-Api-Secret-Token`aiogram 会自动校验。若希望进程退出时删除配置,
再加 `--delete-webhook-on-exit`;默认保留配置,以免普通重启造成更新丢窗。

View file

@ -52,7 +52,7 @@ def parse_args() -> argparse.Namespace:
parser.add_argument(
"--webhook-url",
default=os.getenv("TELESRV_BOT_WEBHOOK_URL", ""),
help="Public HTTPS URL including the webhook path",
help="Public HTTP(S) URL including the webhook path",
)
parser.add_argument(
"--webhook-path",

View file

@ -89,8 +89,8 @@ enable
`/setlogin` 首次创建 client 时只展示一次 OIDC Client Secret不要写进仓库。可用
`/logininfo` 查看 Client ID 和登记结果,或用 `/resetloginsecret` 轮换 secret。
loopback HTTP 仅应配合 telesrv 的显式开发开关使用;测试部署/生产必须换成精确
HTTPS origin
使用 HTTP 域名/IP 时,在 telesrv 配置 `TELESRV_TELEGRAM_LOGIN_ALLOW_HTTP=true`
demo 会接受任意合法 HTTP(S) issuer/public origin不再限制为 loopback
把一次性 secret 和 Client ID 放入进程环境,再启动:

View file

@ -68,10 +68,6 @@ class PendingFlow:
code_verifier: str = ""
def _is_loopback(host: str | None) -> bool:
return host in {"127.0.0.1", "::1", "localhost"}
def normalize_web_base(value: str, *, name: str) -> str:
raw = value.strip().rstrip("/")
parsed = urlsplit(raw)
@ -85,8 +81,6 @@ def normalize_web_base(value: str, *, name: str) -> str:
or parsed.path not in {"", "/"}
):
raise ValueError(f"{name} must be an absolute origin without path, query, or fragment")
if parsed.scheme != "https" and not _is_loopback(parsed.hostname):
raise ValueError(f"{name} must use HTTPS except on loopback")
return f"{parsed.scheme}://{parsed.netloc}"

View file

@ -49,8 +49,14 @@ class LoginDemoHelpersTest(unittest.TestCase):
demo.normalize_web_base("http://127.0.0.1:3000", name="RP"),
"http://127.0.0.1:3000",
)
with self.assertRaises(ValueError):
demo.normalize_web_base("http://rp.example", name="RP")
self.assertEqual(
demo.normalize_web_base("http://192.0.2.25:3000", name="RP"),
"http://192.0.2.25:3000",
)
self.assertEqual(
demo.normalize_web_base("http://rp.example:18080", name="RP"),
"http://rp.example:18080",
)
with self.assertRaises(ValueError):
demo.normalize_web_base("https://rp.example/callback", name="RP")
self.assertEqual(demo.parse_listen("127.0.0.1:3000"), ("127.0.0.1", 3000))

View file

@ -366,7 +366,7 @@ func run(logger *zap.Logger) error {
}
telegramLoginService, err = telegramloginapp.NewService(postgres.NewTelegramLoginStore(pool), codeSealer, telegramloginapp.Config{
Issuer: cfg.TelegramLoginIssuer, AppScheme: cfg.PublicAppScheme,
AllowLoopbackHTTP: cfg.TelegramLoginAllowLoopbackHTTP,
AllowHTTP: cfg.TelegramLoginAllowHTTP,
ClientSecretPepper: clientSecretPepper,
SupportedSigningAlgorithms: signingKeys.ActiveAlgorithms(),
RequestTTL: cfg.TelegramLoginRequestTTL, CodeTTL: cfg.TelegramLoginCodeTTL,
@ -375,7 +375,7 @@ func run(logger *zap.Logger) error {
return fmt.Errorf("initialize telegram login service: %w", err)
}
telegramLoginIDTokens, err = telegramloginapp.NewIDTokenIssuer(signingKeys, telegramloginapp.IDTokenIssuerConfig{
Issuer: cfg.TelegramLoginIssuer, TTL: cfg.TelegramLoginIDTokenTTL,
Issuer: cfg.TelegramLoginIssuer, TTL: cfg.TelegramLoginIDTokenTTL, AllowHTTP: cfg.TelegramLoginAllowHTTP,
})
if err != nil {
return fmt.Errorf("initialize telegram login ID-token issuer: %w", err)
@ -393,7 +393,7 @@ func run(logger *zap.Logger) error {
Service: telegramLoginService, Tokens: telegramLoginIDTokens,
Limiter: redisstore.NewRateLimiter(rdb), AppName: cfg.PublicAppName,
Logger: logger.Named("telegram-login-http"), TrustedProxyCIDRs: cfg.TelegramLoginTrustedProxyCIDRs,
AllowLoopbackHTTP: cfg.TelegramLoginAllowLoopbackHTTP,
AllowHTTP: cfg.TelegramLoginAllowHTTP,
})
if err != nil {
return fmt.Errorf("initialize telegram login HTTP provider: %w", err)