fix(provider): 隐藏 Windsurf refresh token 刷新入口

This commit is contained in:
Entropy.Xu
2026-05-18 19:12:36 +08:00
parent 208c77a062
commit 0a0a8b31c7
5 changed files with 39 additions and 7 deletions

View File

@@ -50,6 +50,7 @@ pub(crate) struct ProviderKeyAuthSemantics {
credential_kind: ProviderKeyCredentialKind, credential_kind: ProviderKeyCredentialKind,
runtime_auth_kind: ProviderKeyRuntimeAuthKind, runtime_auth_kind: ProviderKeyRuntimeAuthKind,
oauth_managed: bool, oauth_managed: bool,
can_refresh_oauth: bool,
} }
impl ProviderKeyAuthSemantics { impl ProviderKeyAuthSemantics {
@@ -66,7 +67,7 @@ impl ProviderKeyAuthSemantics {
} }
pub(crate) const fn can_refresh_oauth(self) -> bool { pub(crate) const fn can_refresh_oauth(self) -> bool {
self.oauth_managed self.can_refresh_oauth
} }
pub(crate) const fn can_export_oauth(self) -> bool { pub(crate) const fn can_export_oauth(self) -> bool {
@@ -179,10 +180,12 @@ pub(crate) fn provider_key_auth_semantics(
} }
}; };
let provider_type_normalized = provider_type.trim().to_ascii_lowercase();
ProviderKeyAuthSemantics { ProviderKeyAuthSemantics {
credential_kind, credential_kind,
runtime_auth_kind, runtime_auth_kind,
oauth_managed, oauth_managed,
can_refresh_oauth: oauth_managed && provider_type_normalized != "windsurf",
} }
} }
@@ -341,6 +344,7 @@ mod tests {
let semantics = provider_key_auth_semantics(&sample_key("oauth"), "windsurf"); let semantics = provider_key_auth_semantics(&sample_key("oauth"), "windsurf");
assert!(semantics.oauth_managed()); assert!(semantics.oauth_managed());
assert!(!semantics.can_refresh_oauth());
assert_eq!( assert_eq!(
semantics.credential_kind(), semantics.credential_kind(),
ProviderKeyCredentialKind::OAuthSession ProviderKeyCredentialKind::OAuthSession

View File

@@ -1,6 +1,7 @@
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { import {
canRefreshOAuthCredential,
getProviderMaskedSecretLabel, getProviderMaskedSecretLabel,
shouldShowOAuthRefreshControl, shouldShowOAuthRefreshControl,
} from '@/utils/providerKeyAuth' } from '@/utils/providerKeyAuth'
@@ -17,14 +18,25 @@ describe('providerKeyAuth', () => {
expect(shouldShowOAuthRefreshControl(key, 'grok')).toBe(false) expect(shouldShowOAuthRefreshControl(key, 'grok')).toBe(false)
}) })
it('keeps standard OAuth providers on OAuth token semantics', () => { it('hides oauth refresh control when backend marks a provider as non-refreshable', () => {
const key = { const input = {
auth_type: 'oauth', auth_type: 'oauth',
oauth_managed: true, oauth_managed: true,
can_refresh_oauth: false, can_refresh_oauth: false,
} }
expect(getProviderMaskedSecretLabel(key, 'codex')).toBe('[OAuth Token]') expect(canRefreshOAuthCredential(input)).toBe(false)
expect(shouldShowOAuthRefreshControl(key, 'codex')).toBe(true) expect(shouldShowOAuthRefreshControl(input)).toBe(false)
})
it('keeps legacy oauth refresh control visible when backend capability is absent', () => {
const input = {
auth_type: 'oauth',
oauth_managed: true,
}
expect(canRefreshOAuthCredential(input)).toBe(true)
expect(shouldShowOAuthRefreshControl(input)).toBe(true)
expect(getProviderMaskedSecretLabel(input, 'codex')).toBe('[OAuth Token]')
}) })
}) })

View File

@@ -251,4 +251,20 @@ describe('providerKeyStatus', () => {
}) })
expect(getOAuthStatusTitle(input, 0)).toBe('Refresh Token 未添加,无法自动刷新') expect(getOAuthStatusTitle(input, 0)).toBe('Refresh Token 未添加,无法自动刷新')
}) })
it('does not treat non-refreshable provider sessions as missing refresh token', () => {
const input = {
auth_type: 'oauth',
oauth_managed: true,
can_refresh_oauth: false,
}
expect(getOAuthStatusDisplayWithFallback(input, 0)).toEqual({
text: '有效期未知',
isExpired: false,
isExpiringSoon: false,
isInvalid: false,
})
expect(getOAuthStatusTitle(input, 0)).toBe('Token 有效期未知')
})
}) })

View File

@@ -92,7 +92,7 @@ export function shouldShowOAuthRefreshControl(
providerType?: string | null, providerType?: string | null,
): boolean { ): boolean {
if (isGrokSessionCredential(input, providerType)) return false if (isGrokSessionCredential(input, providerType)) return false
return isOAuthManagedCredential(input) return canRefreshOAuthCredential(input)
} }
export function canExportOAuthCredential(input: ProviderKeyAuthCarrier): boolean { export function canExportOAuthCredential(input: ProviderKeyAuthCarrier): boolean {

View File

@@ -202,7 +202,7 @@ function mergeOAuthStatusDisplay(
} }
function isOAuthCredentialWithoutRefreshToken(input: ProviderKeyStatusCarrier): boolean { function isOAuthCredentialWithoutRefreshToken(input: ProviderKeyStatusCarrier): boolean {
return isOAuthManagedCredential(input) && !canRefreshOAuthCredential(input) return isOAuthManagedCredential(input) && input.oauth_temporary === true
} }
function getMissingRefreshTokenStatus(): OAuthStatusInfo { function getMissingRefreshTokenStatus(): OAuthStatusInfo {