Skip to content

Commit e83dc3b

Browse files
committed
cache refresh-token for future usage
This avoid going thru the browser workflow again and again. Note: the refresh token is stored in 'git-credential-store', which is not encrypted but it is available with all git standard install. We might want to change that in the future.
1 parent b2f6ea0 commit e83dc3b

File tree

2 files changed

+65
-2
lines changed

2 files changed

+65
-2
lines changed

internal/git/git.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package git
22

33
import (
44
"bytes"
5+
"fmt"
56
_url "net/url"
67
"os"
78
"os/exec"
9+
"regexp"
810
"strings"
911
"syscall"
1012

@@ -50,3 +52,42 @@ func PassThruRemoteHTTPSHelper(remote, url string) {
5052
log.Fatal().Msgf("passThruRemoteHTTPSHelper - %s", err.Error())
5153
}
5254
}
55+
56+
func StoreCredentials(protocol, host, username, password string) error {
57+
var stdin bytes.Buffer
58+
59+
cmd := exec.Command(GitBinary, "credential-store", "store")
60+
// see: https://git-scm.com/docs/git-credential
61+
params := fmt.Sprintf("protocol=%s\nhost=%s\nusername=%s\npassword=%s\n", protocol, host, username, password)
62+
if _, err := stdin.Write([]byte(params)); err != nil {
63+
return err
64+
}
65+
cmd.Stdin = &stdin
66+
res := cmd.Run()
67+
if res == nil {
68+
log.Debug().Msgf("StoreCredentials - credentials saved for protocol=%s,host=%s,username=%s", protocol, host, username)
69+
}
70+
return res
71+
}
72+
73+
func GetCredentials(protocol, host, username string) (string, error) {
74+
var stdin, stdout bytes.Buffer
75+
76+
cmd := exec.Command(GitBinary, "credential-store", "get")
77+
// see: https://git-scm.com/docs/git-credential
78+
params := fmt.Sprintf("protocol=%s\nhost=%s\nusername=%s\n", protocol, host, username)
79+
stdin.Write([]byte(params))
80+
cmd.Stdin = &stdin
81+
cmd.Stdout = &stdout
82+
if err := cmd.Run(); err != nil {
83+
return "", err
84+
}
85+
86+
match := regexp.MustCompile("password=(.*)").FindStringSubmatch(string(stdout.Bytes()))
87+
if match != nil {
88+
log.Debug().Msgf("GetCredentials - found credentials for protocol=%s,host=%s,username=%s", protocol, host, username)
89+
return match[1], nil
90+
}
91+
92+
return "", fmt.Errorf("GetCredentials - not found for protocol=%s,host=%s,username=%s", protocol, host, username)
93+
}

internal/iap/token.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http"
88
"net/url"
99

10+
"github.com/adohkan/git-remote-https-iap/internal/git"
1011
"github.com/int128/oauth2cli"
1112
"github.com/pkg/browser"
1213
"github.com/rs/zerolog/log"
@@ -15,6 +16,11 @@ import (
1516
"golang.org/x/sync/errgroup"
1617
)
1718

19+
const (
20+
CacheProtocol = "iap"
21+
CacheUsername = "refresh-token"
22+
)
23+
1824
type Token struct {
1925
AccessToken string `json:"access_token"`
2026
ExpiresIn int `json:"expires_in"`
@@ -81,13 +87,29 @@ func getRefreshTokenFromBrowserFlow(domain, helperID, helperSecret string) (stri
8187
return token.RefreshToken, nil
8288
}
8389

90+
func cacheRefreshToken(key, token string) error {
91+
return git.StoreCredentials(CacheProtocol, key, CacheUsername, token)
92+
}
93+
94+
func getRefreshTokenFromCache(key string) (string, error) {
95+
return git.GetCredentials(CacheProtocol, key, CacheUsername)
96+
}
97+
8498
// GetIAPAuthToken returns a raw IAP auth token for the given args
8599
func GetIAPAuthToken(domain, helperID, helperSecret, IAPclientID string) (string, error) {
86100
var result Token
87101

88-
refreshToken, err := getRefreshTokenFromBrowserFlow(domain, helperID, helperSecret)
102+
refreshToken, err := getRefreshTokenFromCache(domain)
89103
if err != nil {
90-
return "", err
104+
log.Debug().Msgf("no cached refresh token for %s: %s", domain, err.Error())
105+
106+
refreshToken, err = getRefreshTokenFromBrowserFlow(domain, helperID, helperSecret)
107+
if err != nil {
108+
return "", err
109+
}
110+
if err := cacheRefreshToken(domain, refreshToken); err != nil {
111+
log.Warn().Msgf("could not cache refresh token for %s: %s", domain, err.Error())
112+
}
91113
}
92114

93115
// exchange our refreshToken for an id_token that we can use as GCP_IAAP_AUTH_TOKEN

0 commit comments

Comments
 (0)