Skip to content

Commit d2a3cc6

Browse files
committed
Merge remote-tracking branch 'upstream/main' into sync/upstream-main-20260521-194611
2 parents 365bb4f + 16793d3 commit d2a3cc6

64 files changed

Lines changed: 3373 additions & 122 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ Register now via <a href="https://pateway.ai/?ch=1tsfr51">this link</a> to recei
120120
</td>
121121
</tr>
122122

123+
<tr>
124+
<td width="180"><a href="https://runapi.co/register?aff=fu2E"><img src="assets/partners/logos/runapi.png" alt="RunAPI" width="150"></a></td>
125+
<td>Thanks to RunAPI for sponsoring this project! <a href="https://runapi.co/register?aff=fu2E">RunAPI</a> is an efficient and stable API platform and OpenRouter alternative. With one API Key, you can access 150+ popular models including OpenAI, Claude, Gemini, DeepSeek, and Grok, with pricing as low as 10% of the original rate. It is highly stable and seamlessly compatible with tools such as Claude Code and OpenClaw.
126+
</td>
127+
</tr>
128+
123129
</table>
124130

125131
## Ecosystem

README_CN.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ Sub2API 是一个 AI API 网关平台,用于分发和管理 AI 产品订阅的
112112
<td>感谢 PPToken.org 赞助本项目! <a href="https://api.pptoken.org/register?promo=SUB2API">PPToken.org</a> 主打 GPT 系列模型 API 中转服务,支持 Codex、Claude Code、OpenAI 兼容客户端及 Gemini CLI 等工具接入。充值 1:1,1 元=1 美元额度;GPT 模型最低 0.16 倍倍率,综合成本约为官方价格的 0.22 折,最快首字 Token 约 1 秒,适合开发者低成本、高响应速度接入 GPT 模型能力。技术支持: 7×24 小时真人响应(不是机器人),群内@技术,10 分钟内有回复 。赞助商福利:前 200 名用户通过 <a href="https://api.pptoken.org/register?promo=SUB2API">[专属注册链接]</a> 注册,输入优惠码 `SUB2API`,可领取 Codex / Claude Code 免费试用额度,无门槛、不绑卡。
113113
</td>
114114
</tr>
115+
116+
<tr>
117+
<td width="180"><a href="https://runapi.co/register?aff=fu2E"><img src="assets/partners/logos/runapi.png" alt="RunAPI" width="150"></a></td>
118+
<td>感谢 RunAPI 赞助本项目! <a href="https://runapi.co/register?aff=fu2E">RunAPI</a> 是高效稳定的API OpenRouter平替平台,一个 API Key 即可访问 OpenAI、Claude、Gemini、DeepSeek、Grok 等 150+ 主流模型,低至 1 折,极其稳定,可以无缝兼容 Claude Code、OpenClaw 等工具。
119+
</td>
120+
</tr>
121+
115122
</table>
116123

117124
## 生态项目

README_JA.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ Sub2API は、AI 製品のサブスクリプションから API クォータを
113113
</td>
114114
</tr>
115115

116+
<tr>
117+
<td width="180"><a href="https://runapi.co/register?aff=fu2E"><img src="assets/partners/logos/runapi.png" alt="RunAPI" width="150"></a></td>
118+
<td>RunAPI のご支援に感謝します!<a href="https://runapi.co/register?aff=fu2E">RunAPI</a> は効率的で安定した API プラットフォームで、OpenRouter の代替として利用できます。1つの API キーで OpenAI、Claude、Gemini、DeepSeek、Grok など 150以上の主要モデルにアクセスでき、価格は最低 10% から。非常に安定しており、Claude Code や OpenClaw などのツールとシームレスに互換します。
119+
</td>
120+
</tr>
121+
116122
</table>
117123

118124
## エコシステム

assets/partners/logos/runapi.png

12.8 KB
Loading

backend/cmd/server/wire_gen.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/internal/config/config.go

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net/url"
1010
"os"
1111
"strings"
12+
"sync/atomic"
1213
"time"
1314

1415
"github.com/spf13/viper"
@@ -573,11 +574,35 @@ type CORSConfig struct {
573574
}
574575

575576
type SecurityConfig struct {
576-
URLAllowlist URLAllowlistConfig `mapstructure:"url_allowlist"`
577-
ResponseHeaders ResponseHeaderConfig `mapstructure:"response_headers"`
578-
CSP CSPConfig `mapstructure:"csp"`
579-
ProxyFallback ProxyFallbackConfig `mapstructure:"proxy_fallback"`
580-
ProxyProbe ProxyProbeConfig `mapstructure:"proxy_probe"`
577+
URLAllowlist URLAllowlistConfig `mapstructure:"url_allowlist"`
578+
ResponseHeaders ResponseHeaderConfig `mapstructure:"response_headers"`
579+
CSP CSPConfig `mapstructure:"csp"`
580+
ProxyFallback ProxyFallbackConfig `mapstructure:"proxy_fallback"`
581+
ProxyProbe ProxyProbeConfig `mapstructure:"proxy_probe"`
582+
TrustForwardedIPForAPIKeyACL bool `mapstructure:"trust_forwarded_ip_for_api_key_acl"`
583+
trustForwardedIPForAPIKeyACLLive *atomic.Bool `mapstructure:"-"`
584+
}
585+
586+
func (c *Config) TrustForwardedIPForAPIKeyACL() bool {
587+
if c == nil {
588+
return false
589+
}
590+
live := c.Security.trustForwardedIPForAPIKeyACLLive
591+
if live == nil {
592+
return c.Security.TrustForwardedIPForAPIKeyACL
593+
}
594+
return live.Load()
595+
}
596+
597+
func (c *Config) SetTrustForwardedIPForAPIKeyACL(enabled bool) {
598+
if c == nil {
599+
return
600+
}
601+
c.Security.TrustForwardedIPForAPIKeyACL = enabled
602+
if c.Security.trustForwardedIPForAPIKeyACLLive == nil {
603+
c.Security.trustForwardedIPForAPIKeyACLLive = &atomic.Bool{}
604+
}
605+
c.Security.trustForwardedIPForAPIKeyACLLive.Store(enabled)
581606
}
582607

583608
type URLAllowlistConfig struct {
@@ -1363,6 +1388,7 @@ func load(allowMissingJWTSecret bool) (*Config, error) {
13631388
cfg.Security.ResponseHeaders.AdditionalAllowed = normalizeStringSlice(cfg.Security.ResponseHeaders.AdditionalAllowed)
13641389
cfg.Security.ResponseHeaders.ForceRemove = normalizeStringSlice(cfg.Security.ResponseHeaders.ForceRemove)
13651390
cfg.Security.CSP.Policy = strings.TrimSpace(cfg.Security.CSP.Policy)
1391+
cfg.SetTrustForwardedIPForAPIKeyACL(cfg.Security.TrustForwardedIPForAPIKeyACL)
13661392
cfg.Log.Level = strings.ToLower(strings.TrimSpace(cfg.Log.Level))
13671393
cfg.Log.Format = strings.ToLower(strings.TrimSpace(cfg.Log.Format))
13681394
cfg.Log.ServiceName = strings.TrimSpace(cfg.Log.ServiceName)
@@ -1507,6 +1533,7 @@ func setDefaults() {
15071533
viper.SetDefault("security.csp.enabled", true)
15081534
viper.SetDefault("security.csp.policy", DefaultCSPPolicy)
15091535
viper.SetDefault("security.proxy_probe.insecure_skip_verify", false)
1536+
viper.SetDefault("security.trust_forwarded_ip_for_api_key_acl", false)
15101537

15111538
// Security - disable direct fallback on proxy error
15121539
viper.SetDefault("security.proxy_fallback.allow_direct_on_error", false)

backend/internal/handler/admin/redeem_handler.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,51 @@ func (h *RedeemHandler) BatchDelete(c *gin.Context) {
307307
})
308308
}
309309

310+
// BatchUpdate handles batch updating redeem codes
311+
// POST /api/v1/admin/redeem-codes/batch-update
312+
func (h *RedeemHandler) BatchUpdate(c *gin.Context) {
313+
if h.redeemService == nil {
314+
response.InternalError(c, "redeem service not configured")
315+
return
316+
}
317+
318+
var req dto.BatchUpdateRedeemCodesRequest
319+
if err := c.ShouldBindJSON(&req); err != nil {
320+
response.BadRequest(c, "Invalid request: "+err.Error())
321+
return
322+
}
323+
324+
result, err := h.redeemService.BatchUpdate(c.Request.Context(), &service.RedeemCodeBatchUpdateInput{
325+
IDs: req.IDs,
326+
Fields: redeemBatchUpdateFieldsFromDTO(req.Fields),
327+
})
328+
if err != nil {
329+
response.ErrorFrom(c, err)
330+
return
331+
}
332+
333+
response.Success(c, gin.H{
334+
"updated": result.Updated,
335+
"message": "Redeem codes updated successfully",
336+
})
337+
}
338+
339+
func redeemBatchUpdateFieldsFromDTO(in dto.BatchUpdateRedeemCodeFields) service.RedeemCodeBatchUpdateFields {
340+
out := service.RedeemCodeBatchUpdateFields{
341+
Status: in.Status,
342+
Notes: in.Notes,
343+
Type: in.Type,
344+
Value: in.Value,
345+
}
346+
if in.ExpiresAt.Set {
347+
out.ExpiresAt = service.NullableTimeUpdate{Set: true, Value: in.ExpiresAt.Value}
348+
}
349+
if in.GroupID.Set {
350+
out.GroupID = service.NullableInt64Update{Set: true, Value: in.GroupID.Value}
351+
}
352+
return out
353+
}
354+
310355
// Expire handles expiring a redeem code
311356
// POST /api/v1/admin/redeem-codes/:id/expire
312357
func (h *RedeemHandler) Expire(c *gin.Context) {

backend/internal/handler/admin/setting_handler.go

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ func (h *SettingHandler) GetSettings(c *gin.Context) {
142142
TurnstileEnabled: settings.TurnstileEnabled,
143143
TurnstileSiteKey: settings.TurnstileSiteKey,
144144
TurnstileSecretKeyConfigured: settings.TurnstileSecretKeyConfigured,
145+
APIKeyACLTrustForwardedIP: settings.APIKeyACLTrustForwardedIP,
145146
LinuxDoConnectEnabled: settings.LinuxDoConnectEnabled,
146147
LinuxDoConnectClientID: settings.LinuxDoConnectClientID,
147148
LinuxDoConnectClientSecretConfigured: settings.LinuxDoConnectClientSecretConfigured,
@@ -264,6 +265,7 @@ func (h *SettingHandler) GetSettings(c *gin.Context) {
264265
BalanceLowNotifyEnabled: settings.BalanceLowNotifyEnabled,
265266
BalanceLowNotifyThreshold: settings.BalanceLowNotifyThreshold,
266267
BalanceLowNotifyRechargeURL: settings.BalanceLowNotifyRechargeURL,
268+
SubscriptionExpiryNotifyEnabled: settings.SubscriptionExpiryNotifyEnabled,
267269
AccountQuotaNotifyEnabled: settings.AccountQuotaNotifyEnabled,
268270
AccountQuotaNotifyEmails: dto.NotifyEmailEntriesFromService(settings.AccountQuotaNotifyEmails),
269271
PaymentEnabled: paymentCfg.Enabled,
@@ -399,6 +401,9 @@ type UpdateSettingsRequest struct {
399401
TurnstileSiteKey string `json:"turnstile_site_key"`
400402
TurnstileSecretKey string `json:"turnstile_secret_key"`
401403

404+
// API Key IP 访问控制设置
405+
APIKeyACLTrustForwardedIP *bool `json:"api_key_acl_trust_forwarded_ip"`
406+
402407
// LinuxDo Connect OAuth 登录
403408
LinuxDoConnectEnabled bool `json:"linuxdo_connect_enabled"`
404409
LinuxDoConnectClientID string `json:"linuxdo_connect_client_id"`
@@ -582,12 +587,13 @@ type UpdateSettingsRequest struct {
582587
// OpenAI account scheduling
583588
OpenAIAdvancedSchedulerEnabled *bool `json:"openai_advanced_scheduler_enabled"`
584589

585-
// Balance low notification
586-
BalanceLowNotifyEnabled *bool `json:"balance_low_notify_enabled"`
587-
BalanceLowNotifyThreshold *float64 `json:"balance_low_notify_threshold"`
588-
BalanceLowNotifyRechargeURL *string `json:"balance_low_notify_recharge_url"`
589-
AccountQuotaNotifyEnabled *bool `json:"account_quota_notify_enabled"`
590-
AccountQuotaNotifyEmails *[]dto.NotifyEmailEntry `json:"account_quota_notify_emails"`
590+
// 余额不足提醒
591+
BalanceLowNotifyEnabled *bool `json:"balance_low_notify_enabled"`
592+
BalanceLowNotifyThreshold *float64 `json:"balance_low_notify_threshold"`
593+
BalanceLowNotifyRechargeURL *string `json:"balance_low_notify_recharge_url"`
594+
SubscriptionExpiryNotifyEnabled *bool `json:"subscription_expiry_notify_enabled"`
595+
AccountQuotaNotifyEnabled *bool `json:"account_quota_notify_enabled"`
596+
AccountQuotaNotifyEmails *[]dto.NotifyEmailEntry `json:"account_quota_notify_emails"`
591597

592598
// Payment configuration (integrated into settings, full replace)
593599
PaymentEnabled *bool `json:"payment_enabled"`
@@ -1432,28 +1438,34 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
14321438
}
14331439

14341440
settings := &service.SystemSettings{
1435-
RegistrationEnabled: req.RegistrationEnabled,
1436-
EmailVerifyEnabled: req.EmailVerifyEnabled,
1437-
RegistrationEmailSuffixWhitelist: req.RegistrationEmailSuffixWhitelist,
1438-
PromoCodeEnabled: req.PromoCodeEnabled,
1439-
PasswordResetEnabled: req.PasswordResetEnabled,
1440-
FrontendURL: req.FrontendURL,
1441-
InvitationCodeEnabled: req.InvitationCodeEnabled,
1442-
TotpEnabled: req.TotpEnabled,
1443-
LoginAgreementEnabled: req.LoginAgreementEnabled,
1444-
LoginAgreementMode: loginAgreementMode,
1445-
LoginAgreementUpdatedAt: loginAgreementUpdatedAt,
1446-
LoginAgreementDocuments: loginAgreementDocuments,
1447-
SMTPHost: req.SMTPHost,
1448-
SMTPPort: req.SMTPPort,
1449-
SMTPUsername: req.SMTPUsername,
1450-
SMTPPassword: req.SMTPPassword,
1451-
SMTPFrom: req.SMTPFrom,
1452-
SMTPFromName: req.SMTPFromName,
1453-
SMTPUseTLS: req.SMTPUseTLS,
1454-
TurnstileEnabled: req.TurnstileEnabled,
1455-
TurnstileSiteKey: req.TurnstileSiteKey,
1456-
TurnstileSecretKey: req.TurnstileSecretKey,
1441+
RegistrationEnabled: req.RegistrationEnabled,
1442+
EmailVerifyEnabled: req.EmailVerifyEnabled,
1443+
RegistrationEmailSuffixWhitelist: req.RegistrationEmailSuffixWhitelist,
1444+
PromoCodeEnabled: req.PromoCodeEnabled,
1445+
PasswordResetEnabled: req.PasswordResetEnabled,
1446+
FrontendURL: req.FrontendURL,
1447+
InvitationCodeEnabled: req.InvitationCodeEnabled,
1448+
TotpEnabled: req.TotpEnabled,
1449+
LoginAgreementEnabled: req.LoginAgreementEnabled,
1450+
LoginAgreementMode: loginAgreementMode,
1451+
LoginAgreementUpdatedAt: loginAgreementUpdatedAt,
1452+
LoginAgreementDocuments: loginAgreementDocuments,
1453+
SMTPHost: req.SMTPHost,
1454+
SMTPPort: req.SMTPPort,
1455+
SMTPUsername: req.SMTPUsername,
1456+
SMTPPassword: req.SMTPPassword,
1457+
SMTPFrom: req.SMTPFrom,
1458+
SMTPFromName: req.SMTPFromName,
1459+
SMTPUseTLS: req.SMTPUseTLS,
1460+
TurnstileEnabled: req.TurnstileEnabled,
1461+
TurnstileSiteKey: req.TurnstileSiteKey,
1462+
TurnstileSecretKey: req.TurnstileSecretKey,
1463+
APIKeyACLTrustForwardedIP: func() bool {
1464+
if req.APIKeyACLTrustForwardedIP != nil {
1465+
return *req.APIKeyACLTrustForwardedIP
1466+
}
1467+
return previousSettings.APIKeyACLTrustForwardedIP
1468+
}(),
14571469
LinuxDoConnectEnabled: req.LinuxDoConnectEnabled,
14581470
LinuxDoConnectClientID: req.LinuxDoConnectClientID,
14591471
LinuxDoConnectClientSecret: req.LinuxDoConnectClientSecret,
@@ -1669,6 +1681,12 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
16691681
}
16701682
return previousSettings.BalanceLowNotifyRechargeURL
16711683
}(),
1684+
SubscriptionExpiryNotifyEnabled: func() bool {
1685+
if req.SubscriptionExpiryNotifyEnabled != nil {
1686+
return *req.SubscriptionExpiryNotifyEnabled
1687+
}
1688+
return previousSettings.SubscriptionExpiryNotifyEnabled
1689+
}(),
16721690
AccountQuotaNotifyEnabled: func() bool {
16731691
if req.AccountQuotaNotifyEnabled != nil {
16741692
return *req.AccountQuotaNotifyEnabled
@@ -1869,6 +1887,7 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
18691887
TurnstileEnabled: updatedSettings.TurnstileEnabled,
18701888
TurnstileSiteKey: updatedSettings.TurnstileSiteKey,
18711889
TurnstileSecretKeyConfigured: updatedSettings.TurnstileSecretKeyConfigured,
1890+
APIKeyACLTrustForwardedIP: updatedSettings.APIKeyACLTrustForwardedIP,
18721891
LinuxDoConnectEnabled: updatedSettings.LinuxDoConnectEnabled,
18731892
LinuxDoConnectClientID: updatedSettings.LinuxDoConnectClientID,
18741893
LinuxDoConnectClientSecretConfigured: updatedSettings.LinuxDoConnectClientSecretConfigured,
@@ -1989,6 +2008,7 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
19892008
BalanceLowNotifyEnabled: updatedSettings.BalanceLowNotifyEnabled,
19902009
BalanceLowNotifyThreshold: updatedSettings.BalanceLowNotifyThreshold,
19912010
BalanceLowNotifyRechargeURL: updatedSettings.BalanceLowNotifyRechargeURL,
2011+
SubscriptionExpiryNotifyEnabled: updatedSettings.SubscriptionExpiryNotifyEnabled,
19922012
AccountQuotaNotifyEnabled: updatedSettings.AccountQuotaNotifyEnabled,
19932013
AccountQuotaNotifyEmails: dto.NotifyEmailEntriesFromService(updatedSettings.AccountQuotaNotifyEmails),
19942014
PaymentEnabled: updatedPaymentCfg.Enabled,
@@ -2145,6 +2165,9 @@ func diffSettings(before *service.SystemSettings, after *service.SystemSettings,
21452165
if req.TurnstileSecretKey != "" {
21462166
changed = append(changed, "turnstile_secret_key")
21472167
}
2168+
if before.APIKeyACLTrustForwardedIP != after.APIKeyACLTrustForwardedIP {
2169+
changed = append(changed, "api_key_acl_trust_forwarded_ip")
2170+
}
21482171
if before.LinuxDoConnectEnabled != after.LinuxDoConnectEnabled {
21492172
changed = append(changed, "linuxdo_connect_enabled")
21502173
}
@@ -2454,7 +2477,7 @@ func diffSettings(before *service.SystemSettings, after *service.SystemSettings,
24542477
if before.OpenAIAdvancedSchedulerEnabled != after.OpenAIAdvancedSchedulerEnabled {
24552478
changed = append(changed, "openai_advanced_scheduler_enabled")
24562479
}
2457-
// Balance & quota notification
2480+
// 余额、订阅到期与账号限额通知
24582481
if before.BalanceLowNotifyEnabled != after.BalanceLowNotifyEnabled {
24592482
changed = append(changed, "balance_low_notify_enabled")
24602483
}
@@ -2464,6 +2487,9 @@ func diffSettings(before *service.SystemSettings, after *service.SystemSettings,
24642487
if before.BalanceLowNotifyRechargeURL != after.BalanceLowNotifyRechargeURL {
24652488
changed = append(changed, "balance_low_notify_recharge_url")
24662489
}
2490+
if before.SubscriptionExpiryNotifyEnabled != after.SubscriptionExpiryNotifyEnabled {
2491+
changed = append(changed, "subscription_expiry_notify_enabled")
2492+
}
24672493
if before.AccountQuotaNotifyEnabled != after.AccountQuotaNotifyEnabled {
24682494
changed = append(changed, "account_quota_notify_enabled")
24692495
}
@@ -3472,6 +3498,8 @@ func emailTemplateEventOptionsToDTO(events []service.NotificationEmailEventInfo)
34723498
Value: event.Event,
34733499
Label: event.Label,
34743500
Description: event.Description,
3501+
Category: event.Category,
3502+
Optional: event.Optional,
34753503
})
34763504
}
34773505
return items

backend/internal/handler/auth_oauth_pending_flow_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2492,6 +2492,10 @@ func (r *oauthPendingFlowRedeemCodeRepo) Update(ctx context.Context, code *servi
24922492
return err
24932493
}
24942494

2495+
func (r *oauthPendingFlowRedeemCodeRepo) BatchUpdate(context.Context, []int64, service.RedeemCodeBatchUpdateFields) (int64, error) {
2496+
panic("unexpected BatchUpdate call")
2497+
}
2498+
24952499
func (r *oauthPendingFlowRedeemCodeRepo) Delete(context.Context, int64) error {
24962500
panic("unexpected Delete call")
24972501
}

backend/internal/handler/auth_oauth_test_helpers_test.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package handler
22

33
import (
44
"net/http"
5+
"net/http/httptest"
56
"net/url"
67
"testing"
78

@@ -32,6 +33,13 @@ func findCookie(cookies []*http.Cookie, name string) *http.Cookie {
3233
return nil
3334
}
3435

36+
func requireCookieCleared(t *testing.T, recorder *httptest.ResponseRecorder, name string) {
37+
t.Helper()
38+
cookie := findCookie(recorder.Result().Cookies(), name)
39+
require.NotNil(t, cookie)
40+
require.Equal(t, -1, cookie.MaxAge)
41+
}
42+
3543
func decodeCookieValueForTest(t *testing.T, value string) string {
3644
t.Helper()
3745
decoded, err := decodeCookieValue(value)
@@ -40,6 +48,13 @@ func decodeCookieValueForTest(t *testing.T, value string) string {
4048
}
4149

4250
func assertOAuthRedirectError(t *testing.T, location string, errorCode string, errorMessage string) {
51+
t.Helper()
52+
values := parseOAuthRedirectFragment(t, location)
53+
require.Equal(t, errorCode, values.Get("error"))
54+
require.Equal(t, errorMessage, values.Get("error_message"))
55+
}
56+
57+
func parseOAuthRedirectFragment(t *testing.T, location string) url.Values {
4358
t.Helper()
4459
require.NotEmpty(t, location)
4560

@@ -52,6 +67,5 @@ func assertOAuthRedirectError(t *testing.T, location string, errorCode string, e
5267
}
5368
values, err := url.ParseQuery(rawValues)
5469
require.NoError(t, err)
55-
require.Equal(t, errorCode, values.Get("error"))
56-
require.Equal(t, errorMessage, values.Get("error_message"))
70+
return values
5771
}

0 commit comments

Comments
 (0)