ccm,ocm: check credential file writability before token refresh

Refuse to refresh tokens when the credential file is not writable,
preventing server-side invalidation of the old refresh token that
would make the credential permanently unusable after restart.
This commit is contained in:
世界
2026-03-13 03:27:42 +08:00
parent 8e5811a8c7
commit 6829f91a06
8 changed files with 68 additions and 2 deletions
+8
View File
@@ -108,6 +108,14 @@ func readCredentialsFromFile(path string) (*oauthCredentials, error) {
return credentialsContainer.ClaudeAIAuth, nil
}
func checkCredentialFileWritable(path string) error {
file, err := os.OpenFile(path, os.O_WRONLY, 0)
if err != nil {
return err
}
return file.Close()
}
func writeCredentialsToFile(oauthCredentials *oauthCredentials, path string) error {
data, err := json.MarshalIndent(map[string]any{
"claudeAiOauth": oauthCredentials,
+7
View File
@@ -69,6 +69,13 @@ func platformReadCredentials(customPath string) (*oauthCredentials, error) {
return readCredentialsFromFile(defaultPath)
}
func platformCanWriteCredentials(customPath string) error {
if customPath == "" {
return nil
}
return checkCredentialFileWritable(customPath)
}
func platformWriteCredentials(oauthCredentials *oauthCredentials, customPath string) error {
if customPath != "" {
return writeCredentialsToFile(oauthCredentials, customPath)
+11
View File
@@ -13,6 +13,17 @@ func platformReadCredentials(customPath string) (*oauthCredentials, error) {
return readCredentialsFromFile(customPath)
}
func platformCanWriteCredentials(customPath string) error {
if customPath == "" {
var err error
customPath, err = getDefaultCredentialsPath()
if err != nil {
return err
}
}
return checkCredentialFileWritable(customPath)
}
func platformWriteCredentials(oauthCredentials *oauthCredentials, customPath string) error {
if customPath == "" {
var err error
+6 -1
View File
@@ -225,6 +225,11 @@ func (c *defaultCredential) getAccessToken() (string, error) {
return c.credentials.AccessToken, nil
}
err = platformCanWriteCredentials(c.credentialPath)
if err != nil {
return "", E.Cause(err, "credential file not writable, refusing refresh to avoid invalidation")
}
baseCredentials := cloneCredentials(c.credentials)
newCredentials, err := refreshToken(c.httpClient, c.credentials)
if err != nil {
@@ -258,7 +263,7 @@ func (c *defaultCredential) getAccessToken() (string, error) {
err = platformWriteCredentials(newCredentials, c.credentialPath)
if err != nil {
c.logger.Warn("persist refreshed token for ", c.tag, ": ", err)
c.logger.Error("persist refreshed token for ", c.tag, ": ", err)
}
return newCredentials.AccessToken, nil
+8
View File
@@ -55,6 +55,14 @@ func readCredentialsFromFile(path string) (*oauthCredentials, error) {
return &credentials, nil
}
func checkCredentialFileWritable(path string) error {
file, err := os.OpenFile(path, os.O_WRONLY, 0)
if err != nil {
return err
}
return file.Close()
}
func writeCredentialsToFile(credentials *oauthCredentials, path string) error {
data, err := json.MarshalIndent(credentials, "", " ")
if err != nil {
+11
View File
@@ -13,6 +13,17 @@ func platformReadCredentials(customPath string) (*oauthCredentials, error) {
return readCredentialsFromFile(customPath)
}
func platformCanWriteCredentials(customPath string) error {
if customPath == "" {
var err error
customPath, err = getDefaultCredentialsPath()
if err != nil {
return err
}
}
return checkCredentialFileWritable(customPath)
}
func platformWriteCredentials(credentials *oauthCredentials, customPath string) error {
if customPath == "" {
var err error
+11
View File
@@ -13,6 +13,17 @@ func platformReadCredentials(customPath string) (*oauthCredentials, error) {
return readCredentialsFromFile(customPath)
}
func platformCanWriteCredentials(customPath string) error {
if customPath == "" {
var err error
customPath, err = getDefaultCredentialsPath()
if err != nil {
return err
}
}
return checkCredentialFileWritable(customPath)
}
func platformWriteCredentials(credentials *oauthCredentials, customPath string) error {
if customPath == "" {
var err error
+6 -1
View File
@@ -234,6 +234,11 @@ func (c *defaultCredential) getAccessToken() (string, error) {
return c.credentials.getAccessToken(), nil
}
err = platformCanWriteCredentials(c.credentialPath)
if err != nil {
return "", E.Cause(err, "credential file not writable, refusing refresh to avoid invalidation")
}
baseCredentials := cloneCredentials(c.credentials)
newCredentials, err := refreshToken(c.httpClient, c.credentials)
if err != nil {
@@ -265,7 +270,7 @@ func (c *defaultCredential) getAccessToken() (string, error) {
err = platformWriteCredentials(newCredentials, c.credentialPath)
if err != nil {
c.logger.Warn("persist refreshed token for ", c.tag, ": ", err)
c.logger.Error("persist refreshed token for ", c.tag, ": ", err)
}
return newCredentials.getAccessToken(), nil