pkg/auth/autorest_auth.go (60 lines of code) (raw):

/* Copyright (c) Microsoft Corporation. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package auth import ( "context" "fmt" "os" "strings" "time" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/azure" "github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential" "github.com/pkg/errors" "k8s.io/klog/v2" ) // authResult contains the subset of results from token acquisition operation in ConfidentialClientApplication // For details see https://aka.ms/msal-net-authenticationresult type authResult struct { accessToken string expiresOn time.Time grantedScopes []string declinedScopes []string } func NewAuthorizer(config *Config, env *azure.Environment) (autorest.Authorizer, error) { // Azure AD Workload Identity webhook will inject the following env vars: // AZURE_FEDERATED_TOKEN_FILE is the service account token path // AZURE_AUTHORITY_HOST is the AAD authority hostname tokenFilePath := os.Getenv("AZURE_FEDERATED_TOKEN_FILE") authority := os.Getenv("AZURE_AUTHORITY_HOST") if tokenFilePath == "" || authority == "" { return nil, fmt.Errorf("required environment variables not set, AZURE_FEDERATED_TOKEN_FILE: %s, AZURE_AUTHORITY_HOST: %s", tokenFilePath, authority) } cred := confidential.NewCredFromAssertionCallback(func(context.Context, confidential.AssertionRequestOptions) (string, error) { return readJWTFromFS(tokenFilePath) }) // create the confidential client to request an AAD token confidentialClientApp, err := confidential.New( fmt.Sprintf("%s%s/oauth2/token", authority, config.TenantID), config.UserAssignedIdentityID, cred) if err != nil { return nil, fmt.Errorf("failed to create confidential client app: %w", err) } result, err := confidentialClientApp.AcquireTokenByCredential(context.Background(), []string{strings.TrimSuffix(env.ResourceManagerEndpoint, "/") + "/.default"}) if err != nil { klog.ErrorS(err, "failed to acquire token") return autorest.NewBearerAuthorizer(authResult{}), errors.Wrap(err, "failed to acquire token") } return autorest.NewBearerAuthorizer(authResult{ accessToken: result.AccessToken, expiresOn: result.ExpiresOn, grantedScopes: result.GrantedScopes, declinedScopes: result.DeclinedScopes, }), nil } // OAuthToken implements the OAuthTokenProvider interface. It returns the current access token. func (ar authResult) OAuthToken() string { return ar.accessToken } func (a *authResult) WithAuthorization() autorest.PrepareDecorator { return autorest.WithBearerAuthorization(a.accessToken) } // readJWTFromFS reads the jwt from file system func readJWTFromFS(tokenFilePath string) (string, error) { token, err := os.ReadFile(tokenFilePath) if err != nil { return "", err } return string(token), nil }