Skip to content

Commit f72868c

Browse files
authored
fix: disable API client caching for GitHub App connections (#8850)
GitHub App installation tokens expire after ~1 hour, causing admin APIs (remote-scopes, test-connection) to fail silently after token expiry. This fix overrides GetHash() to return empty string for GitHub App connections, disabling client caching and ensuring fresh tokens are fetched for each admin request. PAT connections continue to use default caching behavior. Fixes #8847
1 parent c8a7711 commit f72868c

2 files changed

Lines changed: 52 additions & 0 deletions

File tree

backend/plugins/github/models/connection.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,16 @@ func (connection GithubConnection) TableName() string {
134134
return "_tool_github_connections"
135135
}
136136

137+
func (connection GithubConnection) GetHash() string {
138+
if connection.AuthMethod == AppKey {
139+
// GitHub App installation tokens expire after ~1 hour, disable API client caching
140+
// to ensure fresh tokens are fetched for admin APIs (remote-scopes, test-connection)
141+
return ""
142+
}
143+
// Use default caching for PAT connections (they don't expire)
144+
return connection.BaseConnection.GetHash()
145+
}
146+
137147
func (connection *GithubConnection) MergeFromRequest(target *GithubConnection, body map[string]interface{}) error {
138148
modifiedConnection := GithubConnection{}
139149
if err := helper.DecodeMapStruct(body, &modifiedConnection, true); err != nil {

backend/plugins/github/models/connection_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ limitations under the License.
1818
package models
1919

2020
import (
21+
"github.com/apache/incubator-devlake/core/models/common"
2122
"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
2223
"testing"
2324

@@ -238,3 +239,44 @@ func TestTokenTypeClassification(t *testing.T) {
238239
assert.Equal(t, GithubTokenTypeFineGrained, conn.typeIs("github_pat_123"))
239240
assert.Equal(t, GithubTokenTypeUnknown, conn.typeIs("some_other_token"))
240241
}
242+
243+
func TestGithubConnection_GetHash(t *testing.T) {
244+
tests := []struct {
245+
name string
246+
connection GithubConnection
247+
want string
248+
}{
249+
{
250+
name: "GitHub App connection should return empty hash to disable caching",
251+
connection: GithubConnection{
252+
GithubConn: GithubConn{
253+
MultiAuth: api.MultiAuth{
254+
AuthMethod: AppKey,
255+
},
256+
},
257+
},
258+
want: "",
259+
},
260+
{
261+
name: "PAT connection should use default hash",
262+
connection: GithubConnection{
263+
BaseConnection: api.BaseConnection{
264+
Model: common.Model{
265+
ID: 123,
266+
},
267+
},
268+
GithubConn: GithubConn{
269+
MultiAuth: api.MultiAuth{
270+
AuthMethod: AccessToken,
271+
},
272+
},
273+
},
274+
want: "1230001-01-01 00:00:00 +0000 UTC", // ID + zero UpdatedAt
275+
},
276+
}
277+
for _, tt := range tests {
278+
t.Run(tt.name, func(t *testing.T) {
279+
assert.Equalf(t, tt.want, tt.connection.GetHash(), "GetHash()")
280+
})
281+
}
282+
}

0 commit comments

Comments
 (0)