-
Notifications
You must be signed in to change notification settings - Fork 3
/
azure_identity_access_token_provider.go
152 lines (130 loc) · 6.41 KB
/
azure_identity_access_token_provider.go
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package microsoft_kiota_authentication_azure
import (
"context"
"encoding/base64"
"errors"
"strings"
u "net/url"
azcore "github.com/Azure/azure-sdk-for-go/sdk/azcore"
azpolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
absauth "github.com/microsoft/kiota-abstractions-go/authentication"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
)
// AzureIdentityAccessTokenProvider implementation of AccessTokenProvider that supports implementations of TokenCredential from Azure.Identity.
type AzureIdentityAccessTokenProvider struct {
scopes []string
credential azcore.TokenCredential
allowedHostsValidator *absauth.AllowedHostsValidator
// The observation options for the request adapter.
observabilityOptions ObservabilityOptions
isCaeEnabled bool
}
// ObservabilityOptions holds the tracing, metrics and logging configuration for the request adapter
type ObservabilityOptions struct {
}
var LocalhostStrings = [4]string{"localhost", "[::1]", "::1", "127.0.0.1"}
func (o ObservabilityOptions) GetTracerInstrumentationName() string {
return "github.com/microsoft/kiota-authentication-azure-go"
}
// NewAzureIdentityAccessTokenProvider creates a new instance of the AzureIdentityAccessTokenProvider using "<scheme>://<host>/.default" as the default scope.
func NewAzureIdentityAccessTokenProvider(credential azcore.TokenCredential) (*AzureIdentityAccessTokenProvider, error) {
return NewAzureIdentityAccessTokenProviderWithScopes(credential, nil)
}
// NewAzureIdentityAccessTokenProviderWithScopes creates a new instance of the AzureIdentityAccessTokenProvider.
func NewAzureIdentityAccessTokenProviderWithScopes(credential azcore.TokenCredential, scopes []string) (*AzureIdentityAccessTokenProvider, error) {
return NewAzureIdentityAccessTokenProviderWithScopesAndValidHosts(credential, scopes, nil)
}
// NewAzureIdentityAccessTokenProviderWithScopesAndValidHosts creates a new instance of the AzureIdentityAccessTokenProvider.
func NewAzureIdentityAccessTokenProviderWithScopesAndValidHosts(credential azcore.TokenCredential, scopes []string, validHosts []string) (*AzureIdentityAccessTokenProvider, error) {
return NewAzureIdentityAccessTokenProviderWithScopesAndValidHostsAndObservabilityOptions(credential, scopes, validHosts, ObservabilityOptions{})
}
// NewAzureIdentityAccessTokenProviderWithScopesAndValidHosts creates a new instance of the AzureIdentityAccessTokenProvider.
func NewAzureIdentityAccessTokenProviderWithScopesAndValidHostsAndObservabilityOptions(credential azcore.TokenCredential, scopes []string, validHosts []string, observabilityOptions ObservabilityOptions) (*AzureIdentityAccessTokenProvider, error) {
return NewAzureIdentityAccessTokenProviderWithScopesAndValidHostsAndObservabilityOptionsAndIsCaeEnabled(credential, scopes, validHosts, observabilityOptions, true)
}
// NewAzureIdentityAccessTokenProviderWithScopesAndValidHostsAndObservabilityOptionsAndIsCaeEnabled creates a new instance of the AzureIdentityAccessTokenProvider.
func NewAzureIdentityAccessTokenProviderWithScopesAndValidHostsAndObservabilityOptionsAndIsCaeEnabled(credential azcore.TokenCredential, scopes []string, validHosts []string, observabilityOptions ObservabilityOptions, isCaeEnabled bool) (*AzureIdentityAccessTokenProvider, error) {
if credential == nil {
return nil, errors.New("credential cannot be nil")
}
scopesLen := len(scopes)
finalScopes := make([]string, scopesLen)
if scopesLen > 0 {
copy(finalScopes, scopes)
}
validator, err := absauth.NewAllowedHostsValidatorErrorCheck(validHosts)
if err != nil {
return nil, err
}
return &AzureIdentityAccessTokenProvider{
credential: credential,
scopes: finalScopes,
allowedHostsValidator: validator,
observabilityOptions: observabilityOptions,
isCaeEnabled: isCaeEnabled,
}, nil
}
const claimsKey = "claims"
// GetAuthorizationToken returns the access token for the provided url.
func (p *AzureIdentityAccessTokenProvider) GetAuthorizationToken(ctx context.Context, url *u.URL, additionalAuthenticationContext map[string]interface{}) (string, error) {
ctx, span := otel.GetTracerProvider().Tracer(p.observabilityOptions.GetTracerInstrumentationName()).Start(ctx, "GetAuthorizationToken")
defer span.End()
if !(*(p.allowedHostsValidator)).IsUrlHostValid(url) {
span.SetAttributes(attribute.Bool("com.microsoft.kiota.authentication.is_url_valid", false))
return "", nil
}
if !strings.EqualFold(url.Scheme, "https") && !isLocalhost(url.Host) {
span.SetAttributes(attribute.Bool("com.microsoft.kiota.authentication.is_url_valid", false))
err := errors.New("url scheme must be https")
span.RecordError(err)
return "", err
}
span.SetAttributes(attribute.Bool("com.microsoft.kiota.authentication.is_url_valid", true))
claims := ""
if additionalAuthenticationContext != nil &&
additionalAuthenticationContext[claimsKey] != nil {
if rawClaims, ok := additionalAuthenticationContext[claimsKey].(string); ok {
decodedClaims, err := base64.StdEncoding.DecodeString(rawClaims)
if err != nil {
span.RecordError(err)
return "", err
}
claims = string(decodedClaims)
err = errors.New("received a claim for CAE but azure identity doesn't support claims: " + claims + " https://github.com/Azure/azure-sdk-for-go/issues/14284")
span.RecordError(err)
return "", err
}
}
span.SetAttributes(attribute.Bool("com.microsoft.kiota.authentication.additional_claims_provided", claims != ""))
if len(p.scopes) == 0 {
p.scopes = append(p.scopes, url.Scheme+"://"+url.Host+"/.default")
}
options := azpolicy.TokenRequestOptions{
Scopes: p.scopes,
EnableCAE: p.isCaeEnabled,
}
span.SetAttributes(attribute.String("com.microsoft.kiota.authentication.scopes", strings.Join(p.scopes, ",")))
token, err := p.credential.GetToken(ctx, options)
if err != nil {
span.RecordError(err)
return "", err
}
return token.Token, nil
}
// GetAllowedHostsValidator returns the hosts validator.
func (p *AzureIdentityAccessTokenProvider) GetAllowedHostsValidator() *absauth.AllowedHostsValidator {
return p.allowedHostsValidator
}
func isLocalhost(host string) bool {
normalizedHost := strings.ToLower(host)
for _, localhostString := range LocalhostStrings {
if strings.HasPrefix(normalizedHost, localhostString) {
return isValidRemainder(strings.TrimPrefix(normalizedHost, localhostString))
}
}
return false
}
func isValidRemainder(remainder string) bool {
return remainder == "" || strings.HasPrefix(remainder, ":")
}