-
Notifications
You must be signed in to change notification settings - Fork 59
Expand file tree
/
Copy pathtokencache.go
More file actions
133 lines (111 loc) · 3.36 KB
/
tokencache.go
File metadata and controls
133 lines (111 loc) · 3.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package oauth
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/rs/zerolog/log"
"golang.org/x/oauth2"
)
// TokenCache defines the interface for storing and retrieving OAuth tokens
type TokenCache interface {
// Save stores a token in the cache
Save(token *oauth2.Token) error
// Load retrieves a token from the cache
Load() (*oauth2.Token, error)
}
// FileTokenCache implements TokenCache using file-based storage
type FileTokenCache struct {
path string
}
// NewFileTokenCache creates a new file-based token cache
func NewFileTokenCache(path string) *FileTokenCache {
return &FileTokenCache{path: path}
}
// Save stores a token in the file cache
func (c *FileTokenCache) Save(token *oauth2.Token) error {
// Create parent directories if they don't exist
if err := os.MkdirAll(filepath.Dir(c.path), 0700); err != nil {
return fmt.Errorf("failed to create token cache directory: %w", err)
}
// Serialize token to JSON
data, err := json.Marshal(token)
if err != nil {
return fmt.Errorf("failed to serialize token: %w", err)
}
// Write to file with secure permissions
if err := os.WriteFile(c.path, data, 0600); err != nil {
return fmt.Errorf("failed to write token to cache: %w", err)
}
log.Debug().Str("path", c.path).Msg("Token saved to cache")
return nil
}
// Load retrieves a token from the file cache
func (c *FileTokenCache) Load() (*oauth2.Token, error) {
data, err := os.ReadFile(c.path)
if err != nil {
if os.IsNotExist(err) {
return nil, nil // Cache doesn't exist yet
}
return nil, fmt.Errorf("failed to read token from cache: %w", err)
}
var token oauth2.Token
if err := json.Unmarshal(data, &token); err != nil {
return nil, fmt.Errorf("failed to deserialize token: %w", err)
}
log.Debug().Str("path", c.path).Msg("Token loaded from cache")
return &token, nil
}
// GetCacheFilePath returns the path to the token cache file
func GetCacheFilePath(host, clientID string, scopes []string) string {
// Create SHA-256 hash of host, client_id, and scopes
h := sha256.New()
h.Write([]byte(host))
h.Write([]byte(clientID))
h.Write([]byte(strings.Join(scopes, ",")))
hash := hex.EncodeToString(h.Sum(nil))
// Get user's home directory
homeDir, err := os.UserHomeDir()
if err != nil {
homeDir = "."
}
return filepath.Join(homeDir, ".config", "databricks-sql-go", "oauth", hash)
}
// CachingTokenSource wraps a TokenSource with a TokenCache
type CachingTokenSource struct {
src oauth2.TokenSource
cache TokenCache
}
// NewCachingTokenSource creates a new TokenSource that caches tokens
func NewCachingTokenSource(src oauth2.TokenSource, cache TokenCache) oauth2.TokenSource {
return &CachingTokenSource{
src: src,
cache: cache,
}
}
// Token returns a valid token from either the cache or the underlying source
func (cts *CachingTokenSource) Token() (*oauth2.Token, error) {
// Try to get token from cache first
if cts.cache != nil {
token, err := cts.cache.Load()
if err == nil && token != nil && token.Valid() {
log.Debug().Msg("Using cached token")
return token, nil
}
}
// Get a new token from the source
token, err := cts.src.Token()
if err != nil {
return nil, err
}
// Save the token to cache
if cts.cache != nil {
if err := cts.cache.Save(token); err != nil {
log.Warn().Err(err).Msg("Failed to save token to cache")
}
}
return token, nil
}