Skip to content

Commit 4fed4f1

Browse files
committed
add AzCliCachedCredential
uses az cli for authentication but caches the tokens instead of calling the az cli again and again Signed-off-by: Markus Blaschke <mail@markus-blaschke.de>
1 parent b6a5eb8 commit 4fed4f1

2 files changed

Lines changed: 77 additions & 0 deletions

File tree

azuresdk/azidentity/credential.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ func NewAzDefaultCredential(clientOptions *azcore.ClientOptions) (azcore.TokenCr
3535
case "az", "cli", "azcli":
3636
// azurecli authentication
3737
return NewAzCliCredential()
38+
case "azcached", "clicached", "azclicached":
39+
// azurecli authentication with token cache
40+
return NewAzCliCachedCredential()
3841
case "devicetoken":
3942
return azidentity.NewDeviceCodeCredential(nil)
4043
case "interactive", "browser", "interactivebrowser":
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package azidentity
2+
3+
import (
4+
"context"
5+
"crypto/sha512"
6+
"encoding/hex"
7+
"encoding/json"
8+
"time"
9+
10+
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
11+
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
12+
"github.com/patrickmn/go-cache"
13+
)
14+
15+
const (
16+
AzCliCachedExpiryTimeNegativeOffset = 5 * time.Minute
17+
)
18+
19+
type (
20+
AzCliCachedCredential struct {
21+
azcore.TokenCredential
22+
cache *cache.Cache
23+
}
24+
)
25+
26+
// NewAzCliCachedCredential returns a new TokenCredential for az cli and token cache
27+
func NewAzCliCachedCredential() (azcore.TokenCredential, error) {
28+
creds, err := NewAzCliCredential()
29+
if err != nil {
30+
return creds, err
31+
}
32+
33+
return AzCliCachedCredential{TokenCredential: creds, cache: cache.New(1*time.Minute, 1*time.Minute)}, nil
34+
}
35+
36+
// GetToken generates a token by calling az cli and caches it if possible for at least 10 minutes
37+
func (c AzCliCachedCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
38+
cacheKey := c.generateOptsCacheHash(opts)
39+
40+
// invalid cache, pass through
41+
if cacheKey == "" {
42+
return c.TokenCredential.GetToken(ctx, opts)
43+
}
44+
45+
// get from cache
46+
if val, exists := c.cache.Get(cacheKey); exists {
47+
if v, ok := val.(azcore.AccessToken); ok {
48+
return v, nil
49+
}
50+
}
51+
52+
token, err := c.TokenCredential.GetToken(ctx, opts)
53+
if err != nil {
54+
return token, err
55+
}
56+
57+
// set cache
58+
expiryDuration := time.Until(token.ExpiresOn) - AzCliCachedExpiryTimeNegativeOffset
59+
c.cache.Set(cacheKey, token, expiryDuration)
60+
61+
return token, nil
62+
}
63+
64+
// generateOptsCacheHash generates a hash from the token options
65+
func (c AzCliCachedCredential) generateOptsCacheHash(opts policy.TokenRequestOptions) string {
66+
hashData, err := json.Marshal(opts)
67+
if err != nil {
68+
return ""
69+
}
70+
71+
hash := sha512.Sum512(hashData)
72+
73+
return hex.EncodeToString(hash[:])
74+
}

0 commit comments

Comments
 (0)