mirror of
https://gitea.com/gitea/tea.git
synced 2026-03-13 09:13:30 +01:00
## Summary - Introduce `github.com/go-authgate/sdk-go/credstore` to store OAuth tokens securely in the OS keyring (macOS Keychain / Linux Secret Service / Windows Credential Manager), with automatic fallback to an encrypted JSON file - Add `AuthMethod` field to `Login` struct; new OAuth logins are marked `auth_method: oauth` and no longer write `token`/`refresh_token`/`token_expiry` to `config.yml` - Add `GetAccessToken()` / `GetRefreshToken()` / `GetTokenExpiry()` accessors that transparently read from credstore for OAuth logins, with fallback to YAML fields for legacy logins - Update all token reference sites across the codebase to use the new accessors - Non-OAuth logins (token, SSH) are completely unaffected; no migration of existing tokens ## Key files | File | Role | |------|------| | `modules/config/credstore.go` | **New** — credstore wrapper (Load/Save/Delete) | | `modules/config/login.go` | Login struct, token accessors, refresh logic | | `modules/auth/oauth.go` | OAuth flow, token creation / re-authentication | | `modules/api/client.go`, `cmd/login/helper.go`, `cmd/login/oauth_refresh.go` | Token reference updates | | `modules/task/pull_*.go`, `modules/task/repo_clone.go` | Git operation token reference updates | ## Test plan - [x] `go build ./...` compiles successfully - [x] `go test ./...` all tests pass - [x] `tea login add --oauth` completes OAuth flow; verify config.yml has `auth_method: oauth` but no token/refresh_token/token_expiry - [x] `tea repos ls` API calls work (token read from credstore) - [x] `tea login delete <name>` credstore token is also removed - [x] Existing non-OAuth logins continue to work unchanged 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: https://gitea.com/gitea/tea/pulls/926 Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com> Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
66 lines
1.8 KiB
Go
66 lines
1.8 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package config
|
|
|
|
import (
|
|
"path/filepath"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/adrg/xdg"
|
|
"github.com/go-authgate/sdk-go/credstore"
|
|
"golang.org/x/oauth2"
|
|
)
|
|
|
|
var (
|
|
tokenStore *credstore.SecureStore[credstore.Token]
|
|
tokenStoreOnce sync.Once
|
|
)
|
|
|
|
func getTokenStore() *credstore.SecureStore[credstore.Token] {
|
|
tokenStoreOnce.Do(func() {
|
|
filePath := filepath.Join(xdg.ConfigHome, "tea", "credentials.json")
|
|
tokenStore = credstore.DefaultTokenSecureStore("tea-cli", filePath)
|
|
})
|
|
return tokenStore
|
|
}
|
|
|
|
// LoadOAuthToken loads OAuth tokens from the secure store.
|
|
func LoadOAuthToken(loginName string) (*credstore.Token, error) {
|
|
tok, err := getTokenStore().Load(loginName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &tok, nil
|
|
}
|
|
|
|
// SaveOAuthToken saves OAuth tokens to the secure store.
|
|
func SaveOAuthToken(loginName, accessToken, refreshToken string, expiresAt time.Time) error {
|
|
return getTokenStore().Save(loginName, credstore.Token{
|
|
AccessToken: accessToken,
|
|
RefreshToken: refreshToken,
|
|
ExpiresAt: expiresAt,
|
|
ClientID: loginName,
|
|
})
|
|
}
|
|
|
|
// DeleteOAuthToken removes tokens from the secure store.
|
|
func DeleteOAuthToken(loginName string) error {
|
|
return getTokenStore().Delete(loginName)
|
|
}
|
|
|
|
// SaveOAuthTokenFromOAuth2 saves an oauth2.Token to credstore, falling back to
|
|
// the existing login's values for empty refresh token or zero expiry.
|
|
func SaveOAuthTokenFromOAuth2(loginName string, token *oauth2.Token, login *Login) error {
|
|
refreshToken := token.RefreshToken
|
|
if refreshToken == "" {
|
|
refreshToken = login.GetRefreshToken()
|
|
}
|
|
expiry := token.Expiry
|
|
if expiry.IsZero() {
|
|
expiry = login.GetTokenExpiry()
|
|
}
|
|
return SaveOAuthToken(loginName, token.AccessToken, refreshToken, expiry)
|
|
}
|