Skip to content

Commit b2f6ea0

Browse files
committed
re-use IAP cookie when present and still valid
1 parent aad44f6 commit b2f6ea0

File tree

2 files changed

+85
-2
lines changed

2 files changed

+85
-2
lines changed

cmd/git-remote-https+iap/main.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
_url "net/url"
66
"os"
7+
"time"
78

89
"github.com/adohkan/git-remote-https-iap/internal/git"
910
"github.com/adohkan/git-remote-https-iap/internal/iap"
@@ -65,9 +66,21 @@ func handleIAPAuthCookieFor(url string) {
6566
log.Error().Msgf("Could not convert %s in https://: %s", url, err)
6667
}
6768

68-
log.Debug().Msgf("Manage IAP auth for %s", os.Args[0], url)
69+
log.Debug().Msgf("Manage IAP auth for %s", url)
6970

70-
if _, err = iap.NewCookie(url); err != nil {
71+
cookie, err := iap.ReadCookie(url)
72+
switch {
73+
case err != nil:
74+
log.Debug().Msgf("could not read IAP cookie for %s: %s", url, err.Error())
75+
cookie, err = iap.NewCookie(url)
76+
case cookie.Expired():
77+
log.Debug().Msgf("IAP cookie for %s has expired", url)
78+
cookie, err = iap.NewCookie(url)
79+
case !cookie.Expired():
80+
log.Debug().Msgf("IAP Cookie still valid until %s", time.Unix(cookie.Claims.ExpiresAt, 0))
81+
}
82+
83+
if err != nil {
7184
log.Fatal().Msg(err.Error())
7285
}
7386
}

internal/iap/cookie.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package iap
22

33
import (
4+
"bufio"
45
"fmt"
56
"net/url"
67
"os"
78
"path/filepath"
9+
"strings"
10+
"time"
811

912
jwt "github.com/dgrijalva/jwt-go"
1013

@@ -25,6 +28,68 @@ type Cookie struct {
2528
Claims jwt.StandardClaims
2629
}
2730

31+
// ReadCookie lookup the http.cookieFile for a given domain and try to load it from the filesystem
32+
func ReadCookie(domain string) (*Cookie, error) {
33+
cookieFile := git.ConfigGetURLMatch("http.cookieFile", domain)
34+
35+
url, err := url.Parse(domain)
36+
if err != nil {
37+
return nil, err
38+
}
39+
40+
c := Cookie{
41+
JarPath: cookieFile,
42+
Domain: url.Host,
43+
}
44+
45+
rawToken, err := c.readRawTokenFromJar()
46+
if err != nil {
47+
return nil, err
48+
}
49+
50+
token, claims, err := parseJWToken(rawToken)
51+
if err != nil {
52+
return nil, err
53+
}
54+
55+
c.Token = token
56+
c.Claims = claims
57+
58+
return &c, nil
59+
}
60+
61+
func (c *Cookie) readRawTokenFromJar() (string, error) {
62+
path := expandHome(c.JarPath)
63+
64+
file, err := os.Open(path)
65+
if err != nil {
66+
return "", err
67+
}
68+
defer file.Close()
69+
70+
scanner := bufio.NewScanner(file)
71+
for scanner.Scan() {
72+
line := scanner.Text()
73+
if strings.HasPrefix(line, "#") || line == "" {
74+
continue
75+
}
76+
fields := strings.Split(line, "\t")
77+
if len(fields) != 7 {
78+
log.Warn().Msgf("readRawTokenFromJar - unexpected format while parsing IAP cookie: %v", line)
79+
continue
80+
}
81+
// see: https://curl.haxx.se/docs/http-cookies.html
82+
cookieName, cookieValue := fields[5], strings.TrimSpace(fields[6])
83+
if cookieName != IAPCookieName {
84+
log.Debug().Msgf("readRawTokenFromJar - skip '%s' while parsing IAP cookie", cookieName)
85+
continue
86+
}
87+
88+
return cookieValue, nil
89+
}
90+
return "", fmt.Errorf("readRawTokenFromJar - %s not found", IAPCookieName)
91+
}
92+
2893
// NewCookie takes care of the authentication workflow and creates the relevant IAP Cookie on the filesystem
2994
func NewCookie(domain string) (*Cookie, error) {
3095

@@ -81,6 +146,11 @@ func (c *Cookie) write(token string, exp int64) error {
81146
return nil
82147
}
83148

149+
// Expired returns a boolean that indicate if the expires-at claim is in the future
150+
func (c *Cookie) Expired() bool {
151+
return c.Claims.ExpiresAt < time.Now().Unix()
152+
}
153+
84154
func parseJWToken(rawToken string) (jwt.Token, jwt.StandardClaims, error) {
85155
var p jwt.Parser
86156
var claims jwt.StandardClaims

0 commit comments

Comments
 (0)