feat(openai): Mobile RT 补全 plan_type、精确匹配账号、刷新时自动设置隐私
1. accounts/check 补全 plan_type:当 id_token 缺少 plan_type(如 Mobile RT), 自动调用 accounts/check 端点获取订阅类型 2. orgID 精确匹配账号:从 JWT 提取 poid 匹配正确账号,避免 Go map 遍历顺序随机导致 plan_type 不稳定 3. RT 刷新时设置隐私:调用 disableOpenAITraining 关闭训练数据共享, 结果存入 extra.privacy_mode,后续跳过重复设置 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,9 +29,10 @@ type soraSessionChunk struct {
|
||||
|
||||
// OpenAIOAuthService handles OpenAI OAuth authentication flows
|
||||
type OpenAIOAuthService struct {
|
||||
sessionStore *openai.SessionStore
|
||||
proxyRepo ProxyRepository
|
||||
oauthClient OpenAIOAuthClient
|
||||
sessionStore *openai.SessionStore
|
||||
proxyRepo ProxyRepository
|
||||
oauthClient OpenAIOAuthClient
|
||||
privacyClientFactory PrivacyClientFactory // 用于调用 chatgpt.com/backend-api(ImpersonateChrome)
|
||||
}
|
||||
|
||||
// NewOpenAIOAuthService creates a new OpenAI OAuth service
|
||||
@@ -43,6 +44,12 @@ func NewOpenAIOAuthService(proxyRepo ProxyRepository, oauthClient OpenAIOAuthCli
|
||||
}
|
||||
}
|
||||
|
||||
// SetPrivacyClientFactory 注入 ImpersonateChrome 客户端工厂,
|
||||
// 用于调用 chatgpt.com/backend-api 获取账号信息(plan_type 等)。
|
||||
func (s *OpenAIOAuthService) SetPrivacyClientFactory(factory PrivacyClientFactory) {
|
||||
s.privacyClientFactory = factory
|
||||
}
|
||||
|
||||
// OpenAIAuthURLResult contains the authorization URL and session info
|
||||
type OpenAIAuthURLResult struct {
|
||||
AuthURL string `json:"auth_url"`
|
||||
@@ -131,6 +138,7 @@ type OpenAITokenInfo struct {
|
||||
ChatGPTUserID string `json:"chatgpt_user_id,omitempty"`
|
||||
OrganizationID string `json:"organization_id,omitempty"`
|
||||
PlanType string `json:"plan_type,omitempty"`
|
||||
PrivacyMode string `json:"privacy_mode,omitempty"`
|
||||
}
|
||||
|
||||
// ExchangeCode exchanges authorization code for tokens
|
||||
@@ -251,6 +259,30 @@ func (s *OpenAIOAuthService) RefreshTokenWithClientID(ctx context.Context, refre
|
||||
tokenInfo.PlanType = userInfo.PlanType
|
||||
}
|
||||
|
||||
// id_token 中缺少 plan_type 时(如 Mobile RT),尝试通过 ChatGPT backend-api 补全
|
||||
if tokenInfo.PlanType == "" && tokenInfo.AccessToken != "" && s.privacyClientFactory != nil {
|
||||
// 从 access_token JWT 中提取 orgID(poid),用于匹配正确的账号
|
||||
orgID := tokenInfo.OrganizationID
|
||||
if orgID == "" {
|
||||
if atClaims, err := openai.DecodeIDToken(tokenInfo.AccessToken); err == nil && atClaims.OpenAIAuth != nil {
|
||||
orgID = atClaims.OpenAIAuth.POID
|
||||
}
|
||||
}
|
||||
if info := fetchChatGPTAccountInfo(ctx, s.privacyClientFactory, tokenInfo.AccessToken, proxyURL, orgID); info != nil {
|
||||
if tokenInfo.PlanType == "" && info.PlanType != "" {
|
||||
tokenInfo.PlanType = info.PlanType
|
||||
}
|
||||
if tokenInfo.Email == "" && info.Email != "" {
|
||||
tokenInfo.Email = info.Email
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试设置隐私(关闭训练数据共享),best-effort
|
||||
if tokenInfo.AccessToken != "" && s.privacyClientFactory != nil {
|
||||
tokenInfo.PrivacyMode = disableOpenAITraining(ctx, s.privacyClientFactory, tokenInfo.AccessToken, proxyURL)
|
||||
}
|
||||
|
||||
return tokenInfo, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user