Files
gochat/backend/internal/oauth/credentialstore/credentialstore.go
T
Rogee 1446cfe701 feat(channels): 社交渠道 OAuth 改进 + 集成应用重命名 + AI 流程文档
- Facebook/Instagram/TikTok 渠道 OAuth 授权流程改进
- 新增 oauth/credentialstore 包
- 集成应用 OpenAI 重命名为 OpenAI 兼容 (migration 061)
- 消息生命周期与 AI 流程架构文档
- inbox 管理界面 i18n 更新
2026-08-01 19:38:27 +08:00

74 lines
1.8 KiB
Go

// Package credentialstore provides a short-lived in-memory cache for OAuth
// credentials that need to be passed from the authorization request to the
// OAuth callback handler.
//
// Usage:
// 1. Authorization handler stores {app_id, app_secret} keyed by a random nonce
// 2. The nonce is embedded in the OAuth state JWT
// 3. Callback handler extracts the nonce from state, retrieves credentials, and the entry auto-expires
//
// Entries expire after 10 minutes. This is a single-instance solution;
// for multi-instance deployments, replace with Redis-backed implementation.
package credentialstore
import (
"crypto/rand"
"encoding/hex"
"sync"
"time"
)
const defaultTTL = 10 * time.Minute
// Entry holds OAuth credentials temporarily for the callback flow.
type Entry struct {
AppID string
AppSecret string
CreatedAt time.Time
}
var (
mu sync.RWMutex
entries = make(map[string]Entry)
)
// Store saves credentials and returns a nonce key for retrieval.
func Store(appID, appSecret string) (string, error) {
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
return "", err
}
nonce := hex.EncodeToString(b)
mu.Lock()
defer mu.Unlock()
// Prune expired entries opportunistically
now := time.Now()
for k, v := range entries {
if now.Sub(v.CreatedAt) > defaultTTL {
delete(entries, k)
}
}
entries[nonce] = Entry{
AppID: appID,
AppSecret: appSecret,
CreatedAt: now,
}
return nonce, nil
}
// Retrieve fetches and deletes credentials by nonce. Returns ok=false if not found or expired.
func Retrieve(nonce string) (Entry, bool) {
mu.Lock()
defer mu.Unlock()
entry, ok := entries[nonce]
if !ok {
return Entry{}, false
}
delete(entries, nonce) // one-time use
if time.Since(entry.CreatedAt) > defaultTTL {
return Entry{}, false
}
return entry, true
}