auth.go raw

   1  package auth
   2  
   3  // Copyright 2017 Microsoft Corporation
   4  //
   5  //  Licensed under the Apache License, Version 2.0 (the "License");
   6  //  you may not use this file except in compliance with the License.
   7  //  You may obtain a copy of the License at
   8  //
   9  //      http://www.apache.org/licenses/LICENSE-2.0
  10  //
  11  //  Unless required by applicable law or agreed to in writing, software
  12  //  distributed under the License is distributed on an "AS IS" BASIS,
  13  //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14  //  See the License for the specific language governing permissions and
  15  //  limitations under the License.
  16  
  17  import (
  18  	"bytes"
  19  	"context"
  20  	"encoding/binary"
  21  	"encoding/json"
  22  	"errors"
  23  	"fmt"
  24  	"io"
  25  	"log"
  26  	"os"
  27  	"strings"
  28  	"unicode/utf16"
  29  
  30  	"github.com/Azure/go-autorest/autorest"
  31  	"github.com/Azure/go-autorest/autorest/adal"
  32  	"github.com/Azure/go-autorest/autorest/azure"
  33  	"github.com/Azure/go-autorest/autorest/azure/cli"
  34  	"github.com/Azure/go-autorest/logger"
  35  	"github.com/dimchansky/utfbom"
  36  )
  37  
  38  // The possible keys in the Values map.
  39  const (
  40  	SubscriptionID          = "AZURE_SUBSCRIPTION_ID"
  41  	TenantID                = "AZURE_TENANT_ID"
  42  	AuxiliaryTenantIDs      = "AZURE_AUXILIARY_TENANT_IDS"
  43  	ClientID                = "AZURE_CLIENT_ID"
  44  	ClientSecret            = "AZURE_CLIENT_SECRET"
  45  	CertificatePath         = "AZURE_CERTIFICATE_PATH"
  46  	CertificatePassword     = "AZURE_CERTIFICATE_PASSWORD"
  47  	Username                = "AZURE_USERNAME"
  48  	Password                = "AZURE_PASSWORD"
  49  	EnvironmentName         = "AZURE_ENVIRONMENT"
  50  	Resource                = "AZURE_AD_RESOURCE"
  51  	ActiveDirectoryEndpoint = "ActiveDirectoryEndpoint"
  52  	ResourceManagerEndpoint = "ResourceManagerEndpoint"
  53  	GraphResourceID         = "GraphResourceID"
  54  	SQLManagementEndpoint   = "SQLManagementEndpoint"
  55  	GalleryEndpoint         = "GalleryEndpoint"
  56  	ManagementEndpoint      = "ManagementEndpoint"
  57  )
  58  
  59  // NewAuthorizerFromEnvironment creates an Authorizer configured from environment variables in the order:
  60  // 1. Client credentials
  61  // 2. Client certificate
  62  // 3. Username password
  63  // 4. MSI
  64  func NewAuthorizerFromEnvironment() (autorest.Authorizer, error) {
  65  	logger.Instance.Writeln(logger.LogInfo, "NewAuthorizerFromEnvironment() determining authentication mechanism")
  66  	settings, err := GetSettingsFromEnvironment()
  67  	if err != nil {
  68  		return nil, err
  69  	}
  70  	return settings.GetAuthorizer()
  71  }
  72  
  73  // NewAuthorizerFromEnvironmentWithResource creates an Authorizer configured from environment variables in the order:
  74  // 1. Client credentials
  75  // 2. Client certificate
  76  // 3. Username password
  77  // 4. MSI
  78  func NewAuthorizerFromEnvironmentWithResource(resource string) (autorest.Authorizer, error) {
  79  	logger.Instance.Writeln(logger.LogInfo, "NewAuthorizerFromEnvironmentWithResource() determining authentication mechanism")
  80  	settings, err := GetSettingsFromEnvironment()
  81  	if err != nil {
  82  		return nil, err
  83  	}
  84  	settings.Values[Resource] = resource
  85  	return settings.GetAuthorizer()
  86  }
  87  
  88  // EnvironmentSettings contains the available authentication settings.
  89  type EnvironmentSettings struct {
  90  	Values      map[string]string
  91  	Environment azure.Environment
  92  }
  93  
  94  // GetSettingsFromEnvironment returns the available authentication settings from the environment.
  95  func GetSettingsFromEnvironment() (s EnvironmentSettings, err error) {
  96  	s = EnvironmentSettings{
  97  		Values: map[string]string{},
  98  	}
  99  	s.setValue(SubscriptionID)
 100  	s.setValue(TenantID)
 101  	s.setValue(AuxiliaryTenantIDs)
 102  	s.setValue(ClientID)
 103  	s.setValue(ClientSecret)
 104  	s.setValue(CertificatePath)
 105  	s.setValue(CertificatePassword)
 106  	s.setValue(Username)
 107  	s.setValue(Password)
 108  	s.setValue(EnvironmentName)
 109  	s.setValue(Resource)
 110  	if v := s.Values[EnvironmentName]; v == "" {
 111  		s.Environment = azure.PublicCloud
 112  	} else {
 113  		s.Environment, err = azure.EnvironmentFromName(v)
 114  	}
 115  	if s.Values[Resource] == "" {
 116  		s.Values[Resource] = s.Environment.ResourceManagerEndpoint
 117  	}
 118  	return
 119  }
 120  
 121  // GetSubscriptionID returns the available subscription ID or an empty string.
 122  func (settings EnvironmentSettings) GetSubscriptionID() string {
 123  	return settings.Values[SubscriptionID]
 124  }
 125  
 126  // adds the specified environment variable value to the Values map if it exists
 127  func (settings EnvironmentSettings) setValue(key string) {
 128  	if v := os.Getenv(key); v != "" {
 129  		logger.Instance.Writef(logger.LogInfo, "GetSettingsFromEnvironment() found environment var %s\n", key)
 130  		settings.Values[key] = v
 131  	}
 132  }
 133  
 134  // helper to return client and tenant IDs
 135  func (settings EnvironmentSettings) getClientAndTenant() (string, string) {
 136  	clientID := settings.Values[ClientID]
 137  	tenantID := settings.Values[TenantID]
 138  	return clientID, tenantID
 139  }
 140  
 141  // GetClientCredentials creates a config object from the available client credentials.
 142  // An error is returned if no client credentials are available.
 143  func (settings EnvironmentSettings) GetClientCredentials() (ClientCredentialsConfig, error) {
 144  	secret := settings.Values[ClientSecret]
 145  	if secret == "" {
 146  		logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetClientCredentials() missing client secret")
 147  		return ClientCredentialsConfig{}, errors.New("missing client secret")
 148  	}
 149  	clientID, tenantID := settings.getClientAndTenant()
 150  	config := NewClientCredentialsConfig(clientID, secret, tenantID)
 151  	config.AADEndpoint = settings.Environment.ActiveDirectoryEndpoint
 152  	config.Resource = settings.Values[Resource]
 153  	if auxTenants, ok := settings.Values[AuxiliaryTenantIDs]; ok {
 154  		config.AuxTenants = strings.Split(auxTenants, ";")
 155  		for i := range config.AuxTenants {
 156  			config.AuxTenants[i] = strings.TrimSpace(config.AuxTenants[i])
 157  		}
 158  	}
 159  	return config, nil
 160  }
 161  
 162  // GetClientCertificate creates a config object from the available certificate credentials.
 163  // An error is returned if no certificate credentials are available.
 164  func (settings EnvironmentSettings) GetClientCertificate() (ClientCertificateConfig, error) {
 165  	certPath := settings.Values[CertificatePath]
 166  	if certPath == "" {
 167  		logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetClientCertificate() missing certificate path")
 168  		return ClientCertificateConfig{}, errors.New("missing certificate path")
 169  	}
 170  	certPwd := settings.Values[CertificatePassword]
 171  	clientID, tenantID := settings.getClientAndTenant()
 172  	config := NewClientCertificateConfig(certPath, certPwd, clientID, tenantID)
 173  	config.AADEndpoint = settings.Environment.ActiveDirectoryEndpoint
 174  	config.Resource = settings.Values[Resource]
 175  	return config, nil
 176  }
 177  
 178  // GetUsernamePassword creates a config object from the available username/password credentials.
 179  // An error is returned if no username/password credentials are available.
 180  func (settings EnvironmentSettings) GetUsernamePassword() (UsernamePasswordConfig, error) {
 181  	username := settings.Values[Username]
 182  	password := settings.Values[Password]
 183  	if username == "" || password == "" {
 184  		logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetUsernamePassword() missing username and/or password")
 185  		return UsernamePasswordConfig{}, errors.New("missing username/password")
 186  	}
 187  	clientID, tenantID := settings.getClientAndTenant()
 188  	config := NewUsernamePasswordConfig(username, password, clientID, tenantID)
 189  	config.AADEndpoint = settings.Environment.ActiveDirectoryEndpoint
 190  	config.Resource = settings.Values[Resource]
 191  	return config, nil
 192  }
 193  
 194  // GetMSI creates a MSI config object from the available client ID.
 195  func (settings EnvironmentSettings) GetMSI() MSIConfig {
 196  	config := NewMSIConfig()
 197  	config.Resource = settings.Values[Resource]
 198  	config.ClientID = settings.Values[ClientID]
 199  	return config
 200  }
 201  
 202  // GetDeviceFlow creates a device-flow config object from the available client and tenant IDs.
 203  func (settings EnvironmentSettings) GetDeviceFlow() DeviceFlowConfig {
 204  	clientID, tenantID := settings.getClientAndTenant()
 205  	config := NewDeviceFlowConfig(clientID, tenantID)
 206  	config.AADEndpoint = settings.Environment.ActiveDirectoryEndpoint
 207  	config.Resource = settings.Values[Resource]
 208  	return config
 209  }
 210  
 211  // GetAuthorizer creates an Authorizer configured from environment variables in the order:
 212  // 1. Client credentials
 213  // 2. Client certificate
 214  // 3. Username password
 215  // 4. MSI
 216  func (settings EnvironmentSettings) GetAuthorizer() (autorest.Authorizer, error) {
 217  	//1.Client Credentials
 218  	if c, e := settings.GetClientCredentials(); e == nil {
 219  		logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetAuthorizer() using client secret credentials")
 220  		return c.Authorizer()
 221  	}
 222  
 223  	//2. Client Certificate
 224  	if c, e := settings.GetClientCertificate(); e == nil {
 225  		logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetAuthorizer() using client certificate credentials")
 226  		return c.Authorizer()
 227  	}
 228  
 229  	//3. Username Password
 230  	if c, e := settings.GetUsernamePassword(); e == nil {
 231  		logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetAuthorizer() using user name/password credentials")
 232  		return c.Authorizer()
 233  	}
 234  
 235  	// 4. MSI
 236  	if !adal.MSIAvailable(context.Background(), nil) {
 237  		return nil, errors.New("MSI not available")
 238  	}
 239  	logger.Instance.Writeln(logger.LogInfo, "EnvironmentSettings.GetAuthorizer() using MSI authentication")
 240  	return settings.GetMSI().Authorizer()
 241  }
 242  
 243  // NewAuthorizerFromFile creates an Authorizer configured from a configuration file in the following order.
 244  // 1. Client credentials
 245  // 2. Client certificate
 246  // The path to the configuration file must be specified in the AZURE_AUTH_LOCATION environment variable.
 247  // resourceBaseURI - used to determine the resource type
 248  func NewAuthorizerFromFile(resourceBaseURI string) (autorest.Authorizer, error) {
 249  	settings, err := GetSettingsFromFile()
 250  	if err != nil {
 251  		return nil, err
 252  	}
 253  	return settings.GetAuthorizer(resourceBaseURI)
 254  }
 255  
 256  // GetAuthorizer create an Authorizer in the following order.
 257  // 1. Client credentials
 258  // 2. Client certificate
 259  // resourceBaseURI - used to determine the resource type
 260  func (settings FileSettings) GetAuthorizer(resourceBaseURI string) (autorest.Authorizer, error) {
 261  	if resourceBaseURI == "" {
 262  		resourceBaseURI = azure.PublicCloud.ServiceManagementEndpoint
 263  	}
 264  	if a, err := settings.ClientCredentialsAuthorizer(resourceBaseURI); err == nil {
 265  		return a, err
 266  	}
 267  	if a, err := settings.ClientCertificateAuthorizer(resourceBaseURI); err == nil {
 268  		return a, err
 269  	}
 270  	return nil, errors.New("auth file missing client and certificate credentials")
 271  }
 272  
 273  // NewAuthorizerFromFileWithResource creates an Authorizer configured from a configuration file in the following order.
 274  // 1. Client credentials
 275  // 2. Client certificate
 276  // The path to the configuration file must be specified in the AZURE_AUTH_LOCATION environment variable.
 277  func NewAuthorizerFromFileWithResource(resource string) (autorest.Authorizer, error) {
 278  	s, err := GetSettingsFromFile()
 279  	if err != nil {
 280  		return nil, err
 281  	}
 282  	if a, err := s.ClientCredentialsAuthorizerWithResource(resource); err == nil {
 283  		return a, err
 284  	}
 285  	if a, err := s.ClientCertificateAuthorizerWithResource(resource); err == nil {
 286  		return a, err
 287  	}
 288  	return nil, errors.New("auth file missing client and certificate credentials")
 289  }
 290  
 291  // NewAuthorizerFromCLI creates an Authorizer configured from Azure CLI 2.0 for local development scenarios.
 292  func NewAuthorizerFromCLI() (autorest.Authorizer, error) {
 293  	settings, err := GetSettingsFromEnvironment()
 294  	if err != nil {
 295  		return nil, err
 296  	}
 297  
 298  	if settings.Values[Resource] == "" {
 299  		settings.Values[Resource] = settings.Environment.ResourceManagerEndpoint
 300  	}
 301  
 302  	return NewAuthorizerFromCLIWithResource(settings.Values[Resource])
 303  }
 304  
 305  // NewAuthorizerFromCLIWithResource creates an Authorizer configured from Azure CLI 2.0 for local development scenarios.
 306  func NewAuthorizerFromCLIWithResource(resource string) (autorest.Authorizer, error) {
 307  	token, err := cli.GetTokenFromCLI(resource)
 308  	if err != nil {
 309  		return nil, err
 310  	}
 311  
 312  	adalToken, err := token.ToADALToken()
 313  	if err != nil {
 314  		return nil, err
 315  	}
 316  
 317  	return autorest.NewBearerAuthorizer(&adalToken), nil
 318  }
 319  
 320  // GetSettingsFromFile returns the available authentication settings from an Azure CLI authentication file.
 321  func GetSettingsFromFile() (FileSettings, error) {
 322  	s := FileSettings{}
 323  	fileLocation := os.Getenv("AZURE_AUTH_LOCATION")
 324  	if fileLocation == "" {
 325  		return s, errors.New("environment variable AZURE_AUTH_LOCATION is not set")
 326  	}
 327  
 328  	contents, err := os.ReadFile(fileLocation)
 329  	if err != nil {
 330  		return s, err
 331  	}
 332  
 333  	// Auth file might be encoded
 334  	decoded, err := decode(contents)
 335  	if err != nil {
 336  		return s, err
 337  	}
 338  
 339  	authFile := map[string]interface{}{}
 340  	err = json.Unmarshal(decoded, &authFile)
 341  	if err != nil {
 342  		return s, err
 343  	}
 344  
 345  	s.Values = map[string]string{}
 346  	s.setKeyValue(ClientID, authFile["clientId"])
 347  	s.setKeyValue(ClientSecret, authFile["clientSecret"])
 348  	s.setKeyValue(CertificatePath, authFile["clientCertificate"])
 349  	s.setKeyValue(CertificatePassword, authFile["clientCertificatePassword"])
 350  	s.setKeyValue(SubscriptionID, authFile["subscriptionId"])
 351  	s.setKeyValue(TenantID, authFile["tenantId"])
 352  	s.setKeyValue(ActiveDirectoryEndpoint, authFile["activeDirectoryEndpointUrl"])
 353  	s.setKeyValue(ResourceManagerEndpoint, authFile["resourceManagerEndpointUrl"])
 354  	s.setKeyValue(GraphResourceID, authFile["activeDirectoryGraphResourceId"])
 355  	s.setKeyValue(SQLManagementEndpoint, authFile["sqlManagementEndpointUrl"])
 356  	s.setKeyValue(GalleryEndpoint, authFile["galleryEndpointUrl"])
 357  	s.setKeyValue(ManagementEndpoint, authFile["managementEndpointUrl"])
 358  	return s, nil
 359  }
 360  
 361  // FileSettings contains the available authentication settings.
 362  type FileSettings struct {
 363  	Values map[string]string
 364  }
 365  
 366  // GetSubscriptionID returns the available subscription ID or an empty string.
 367  func (settings FileSettings) GetSubscriptionID() string {
 368  	return settings.Values[SubscriptionID]
 369  }
 370  
 371  // adds the specified value to the Values map if it isn't nil
 372  func (settings FileSettings) setKeyValue(key string, val interface{}) {
 373  	if val != nil {
 374  		settings.Values[key] = val.(string)
 375  	}
 376  }
 377  
 378  // returns the specified AAD endpoint or the public cloud endpoint if unspecified
 379  func (settings FileSettings) getAADEndpoint() string {
 380  	if v, ok := settings.Values[ActiveDirectoryEndpoint]; ok {
 381  		return v
 382  	}
 383  	return azure.PublicCloud.ActiveDirectoryEndpoint
 384  }
 385  
 386  // ServicePrincipalTokenFromClientCredentials creates a ServicePrincipalToken from the available client credentials.
 387  func (settings FileSettings) ServicePrincipalTokenFromClientCredentials(baseURI string) (*adal.ServicePrincipalToken, error) {
 388  	resource, err := settings.getResourceForToken(baseURI)
 389  	if err != nil {
 390  		return nil, err
 391  	}
 392  	return settings.ServicePrincipalTokenFromClientCredentialsWithResource(resource)
 393  }
 394  
 395  // ClientCredentialsAuthorizer creates an authorizer from the available client credentials.
 396  func (settings FileSettings) ClientCredentialsAuthorizer(baseURI string) (autorest.Authorizer, error) {
 397  	resource, err := settings.getResourceForToken(baseURI)
 398  	if err != nil {
 399  		return nil, err
 400  	}
 401  	return settings.ClientCredentialsAuthorizerWithResource(resource)
 402  }
 403  
 404  // ServicePrincipalTokenFromClientCredentialsWithResource creates a ServicePrincipalToken
 405  // from the available client credentials and the specified resource.
 406  func (settings FileSettings) ServicePrincipalTokenFromClientCredentialsWithResource(resource string) (*adal.ServicePrincipalToken, error) {
 407  	if _, ok := settings.Values[ClientSecret]; !ok {
 408  		return nil, errors.New("missing client secret")
 409  	}
 410  	config, err := adal.NewOAuthConfig(settings.getAADEndpoint(), settings.Values[TenantID])
 411  	if err != nil {
 412  		return nil, err
 413  	}
 414  	return adal.NewServicePrincipalToken(*config, settings.Values[ClientID], settings.Values[ClientSecret], resource)
 415  }
 416  
 417  func (settings FileSettings) clientCertificateConfigWithResource(resource string) (ClientCertificateConfig, error) {
 418  	if _, ok := settings.Values[CertificatePath]; !ok {
 419  		return ClientCertificateConfig{}, errors.New("missing certificate path")
 420  	}
 421  	cfg := NewClientCertificateConfig(settings.Values[CertificatePath], settings.Values[CertificatePassword], settings.Values[ClientID], settings.Values[TenantID])
 422  	cfg.AADEndpoint = settings.getAADEndpoint()
 423  	cfg.Resource = resource
 424  	return cfg, nil
 425  }
 426  
 427  // ClientCredentialsAuthorizerWithResource creates an authorizer from the available client credentials and the specified resource.
 428  func (settings FileSettings) ClientCredentialsAuthorizerWithResource(resource string) (autorest.Authorizer, error) {
 429  	spToken, err := settings.ServicePrincipalTokenFromClientCredentialsWithResource(resource)
 430  	if err != nil {
 431  		return nil, err
 432  	}
 433  	return autorest.NewBearerAuthorizer(spToken), nil
 434  }
 435  
 436  // ServicePrincipalTokenFromClientCertificate creates a ServicePrincipalToken from the available certificate credentials.
 437  func (settings FileSettings) ServicePrincipalTokenFromClientCertificate(baseURI string) (*adal.ServicePrincipalToken, error) {
 438  	resource, err := settings.getResourceForToken(baseURI)
 439  	if err != nil {
 440  		return nil, err
 441  	}
 442  	return settings.ServicePrincipalTokenFromClientCertificateWithResource(resource)
 443  }
 444  
 445  // ClientCertificateAuthorizer creates an authorizer from the available certificate credentials.
 446  func (settings FileSettings) ClientCertificateAuthorizer(baseURI string) (autorest.Authorizer, error) {
 447  	resource, err := settings.getResourceForToken(baseURI)
 448  	if err != nil {
 449  		return nil, err
 450  	}
 451  	return settings.ClientCertificateAuthorizerWithResource(resource)
 452  }
 453  
 454  // ServicePrincipalTokenFromClientCertificateWithResource creates a ServicePrincipalToken from the available certificate credentials.
 455  func (settings FileSettings) ServicePrincipalTokenFromClientCertificateWithResource(resource string) (*adal.ServicePrincipalToken, error) {
 456  	cfg, err := settings.clientCertificateConfigWithResource(resource)
 457  	if err != nil {
 458  		return nil, err
 459  	}
 460  	return cfg.ServicePrincipalToken()
 461  }
 462  
 463  // ClientCertificateAuthorizerWithResource creates an authorizer from the available certificate credentials and the specified resource.
 464  func (settings FileSettings) ClientCertificateAuthorizerWithResource(resource string) (autorest.Authorizer, error) {
 465  	cfg, err := settings.clientCertificateConfigWithResource(resource)
 466  	if err != nil {
 467  		return nil, err
 468  	}
 469  	return cfg.Authorizer()
 470  }
 471  
 472  func decode(b []byte) ([]byte, error) {
 473  	reader, enc := utfbom.Skip(bytes.NewReader(b))
 474  
 475  	switch enc {
 476  	case utfbom.UTF16LittleEndian:
 477  		u16 := make([]uint16, (len(b)/2)-1)
 478  		err := binary.Read(reader, binary.LittleEndian, &u16)
 479  		if err != nil {
 480  			return nil, err
 481  		}
 482  		return []byte(string(utf16.Decode(u16))), nil
 483  	case utfbom.UTF16BigEndian:
 484  		u16 := make([]uint16, (len(b)/2)-1)
 485  		err := binary.Read(reader, binary.BigEndian, &u16)
 486  		if err != nil {
 487  			return nil, err
 488  		}
 489  		return []byte(string(utf16.Decode(u16))), nil
 490  	}
 491  	return io.ReadAll(reader)
 492  }
 493  
 494  func (settings FileSettings) getResourceForToken(baseURI string) (string, error) {
 495  	// Compare default base URI from the SDK to the endpoints from the public cloud
 496  	// Base URI and token resource are the same string. This func finds the authentication
 497  	// file field that matches the SDK base URI. The SDK defines the public cloud
 498  	// endpoint as its default base URI
 499  	if !strings.HasSuffix(baseURI, "/") {
 500  		baseURI += "/"
 501  	}
 502  	switch baseURI {
 503  	case azure.PublicCloud.ServiceManagementEndpoint:
 504  		return settings.Values[ManagementEndpoint], nil
 505  	case azure.PublicCloud.ResourceManagerEndpoint:
 506  		return settings.Values[ResourceManagerEndpoint], nil
 507  	case azure.PublicCloud.ActiveDirectoryEndpoint:
 508  		return settings.Values[ActiveDirectoryEndpoint], nil
 509  	case azure.PublicCloud.GalleryEndpoint:
 510  		return settings.Values[GalleryEndpoint], nil
 511  	case azure.PublicCloud.GraphEndpoint:
 512  		return settings.Values[GraphResourceID], nil
 513  	}
 514  	return "", fmt.Errorf("auth: base URI not found in endpoints")
 515  }
 516  
 517  // NewClientCredentialsConfig creates an AuthorizerConfig object configured to obtain an Authorizer through Client Credentials.
 518  // Defaults to Public Cloud and Resource Manager Endpoint.
 519  func NewClientCredentialsConfig(clientID string, clientSecret string, tenantID string) ClientCredentialsConfig {
 520  	return ClientCredentialsConfig{
 521  		ClientID:     clientID,
 522  		ClientSecret: clientSecret,
 523  		TenantID:     tenantID,
 524  		Resource:     azure.PublicCloud.ResourceManagerEndpoint,
 525  		AADEndpoint:  azure.PublicCloud.ActiveDirectoryEndpoint,
 526  	}
 527  }
 528  
 529  // NewClientCertificateConfig creates a ClientCertificateConfig object configured to obtain an Authorizer through client certificate.
 530  // Defaults to Public Cloud and Resource Manager Endpoint.
 531  func NewClientCertificateConfig(certificatePath string, certificatePassword string, clientID string, tenantID string) ClientCertificateConfig {
 532  	return ClientCertificateConfig{
 533  		CertificatePath:     certificatePath,
 534  		CertificatePassword: certificatePassword,
 535  		ClientID:            clientID,
 536  		TenantID:            tenantID,
 537  		Resource:            azure.PublicCloud.ResourceManagerEndpoint,
 538  		AADEndpoint:         azure.PublicCloud.ActiveDirectoryEndpoint,
 539  	}
 540  }
 541  
 542  // NewUsernamePasswordConfig creates an UsernamePasswordConfig object configured to obtain an Authorizer through username and password.
 543  // Defaults to Public Cloud and Resource Manager Endpoint.
 544  func NewUsernamePasswordConfig(username string, password string, clientID string, tenantID string) UsernamePasswordConfig {
 545  	return UsernamePasswordConfig{
 546  		Username:    username,
 547  		Password:    password,
 548  		ClientID:    clientID,
 549  		TenantID:    tenantID,
 550  		Resource:    azure.PublicCloud.ResourceManagerEndpoint,
 551  		AADEndpoint: azure.PublicCloud.ActiveDirectoryEndpoint,
 552  	}
 553  }
 554  
 555  // NewMSIConfig creates an MSIConfig object configured to obtain an Authorizer through MSI.
 556  func NewMSIConfig() MSIConfig {
 557  	return MSIConfig{
 558  		Resource: azure.PublicCloud.ResourceManagerEndpoint,
 559  	}
 560  }
 561  
 562  // NewDeviceFlowConfig creates a DeviceFlowConfig object configured to obtain an Authorizer through device flow.
 563  // Defaults to Public Cloud and Resource Manager Endpoint.
 564  func NewDeviceFlowConfig(clientID string, tenantID string) DeviceFlowConfig {
 565  	return DeviceFlowConfig{
 566  		ClientID:    clientID,
 567  		TenantID:    tenantID,
 568  		Resource:    azure.PublicCloud.ResourceManagerEndpoint,
 569  		AADEndpoint: azure.PublicCloud.ActiveDirectoryEndpoint,
 570  	}
 571  }
 572  
 573  // AuthorizerConfig provides an authorizer from the configuration provided.
 574  type AuthorizerConfig interface {
 575  	Authorizer() (autorest.Authorizer, error)
 576  }
 577  
 578  // ClientCredentialsConfig provides the options to get a bearer authorizer from client credentials.
 579  type ClientCredentialsConfig struct {
 580  	ClientID     string
 581  	ClientSecret string
 582  	TenantID     string
 583  	AuxTenants   []string
 584  	AADEndpoint  string
 585  	Resource     string
 586  }
 587  
 588  // ServicePrincipalToken creates a ServicePrincipalToken from client credentials.
 589  func (ccc ClientCredentialsConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
 590  	oauthConfig, err := adal.NewOAuthConfig(ccc.AADEndpoint, ccc.TenantID)
 591  	if err != nil {
 592  		return nil, err
 593  	}
 594  	return adal.NewServicePrincipalToken(*oauthConfig, ccc.ClientID, ccc.ClientSecret, ccc.Resource)
 595  }
 596  
 597  // MultiTenantServicePrincipalToken creates a MultiTenantServicePrincipalToken from client credentials.
 598  func (ccc ClientCredentialsConfig) MultiTenantServicePrincipalToken() (*adal.MultiTenantServicePrincipalToken, error) {
 599  	oauthConfig, err := adal.NewMultiTenantOAuthConfig(ccc.AADEndpoint, ccc.TenantID, ccc.AuxTenants, adal.OAuthOptions{})
 600  	if err != nil {
 601  		return nil, err
 602  	}
 603  	return adal.NewMultiTenantServicePrincipalToken(oauthConfig, ccc.ClientID, ccc.ClientSecret, ccc.Resource)
 604  }
 605  
 606  // Authorizer gets the authorizer from client credentials.
 607  func (ccc ClientCredentialsConfig) Authorizer() (autorest.Authorizer, error) {
 608  	if len(ccc.AuxTenants) == 0 {
 609  		spToken, err := ccc.ServicePrincipalToken()
 610  		if err != nil {
 611  			return nil, fmt.Errorf("failed to get SPT from client credentials: %v", err)
 612  		}
 613  		return autorest.NewBearerAuthorizer(spToken), nil
 614  	}
 615  	mtSPT, err := ccc.MultiTenantServicePrincipalToken()
 616  	if err != nil {
 617  		return nil, fmt.Errorf("failed to get multitenant SPT from client credentials: %v", err)
 618  	}
 619  	return autorest.NewMultiTenantServicePrincipalTokenAuthorizer(mtSPT), nil
 620  }
 621  
 622  // ClientCertificateConfig provides the options to get a bearer authorizer from a client certificate.
 623  type ClientCertificateConfig struct {
 624  	ClientID            string
 625  	CertificatePath     string
 626  	CertificatePassword string
 627  	TenantID            string
 628  	AuxTenants          []string
 629  	AADEndpoint         string
 630  	Resource            string
 631  }
 632  
 633  // ServicePrincipalToken creates a ServicePrincipalToken from client certificate.
 634  func (ccc ClientCertificateConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
 635  	oauthConfig, err := adal.NewOAuthConfig(ccc.AADEndpoint, ccc.TenantID)
 636  	if err != nil {
 637  		return nil, err
 638  	}
 639  	certData, err := os.ReadFile(ccc.CertificatePath)
 640  	if err != nil {
 641  		return nil, fmt.Errorf("failed to read the certificate file (%s): %v", ccc.CertificatePath, err)
 642  	}
 643  	certificate, rsaPrivateKey, err := adal.DecodePfxCertificateData(certData, ccc.CertificatePassword)
 644  	if err != nil {
 645  		return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err)
 646  	}
 647  	return adal.NewServicePrincipalTokenFromCertificate(*oauthConfig, ccc.ClientID, certificate, rsaPrivateKey, ccc.Resource)
 648  }
 649  
 650  // MultiTenantServicePrincipalToken creates a MultiTenantServicePrincipalToken from client certificate.
 651  func (ccc ClientCertificateConfig) MultiTenantServicePrincipalToken() (*adal.MultiTenantServicePrincipalToken, error) {
 652  	oauthConfig, err := adal.NewMultiTenantOAuthConfig(ccc.AADEndpoint, ccc.TenantID, ccc.AuxTenants, adal.OAuthOptions{})
 653  	if err != nil {
 654  		return nil, err
 655  	}
 656  	certData, err := os.ReadFile(ccc.CertificatePath)
 657  	if err != nil {
 658  		return nil, fmt.Errorf("failed to read the certificate file (%s): %v", ccc.CertificatePath, err)
 659  	}
 660  	certificate, rsaPrivateKey, err := adal.DecodePfxCertificateData(certData, ccc.CertificatePassword)
 661  	if err != nil {
 662  		return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err)
 663  	}
 664  	return adal.NewMultiTenantServicePrincipalTokenFromCertificate(oauthConfig, ccc.ClientID, certificate, rsaPrivateKey, ccc.Resource)
 665  }
 666  
 667  // Authorizer gets an authorizer object from client certificate.
 668  func (ccc ClientCertificateConfig) Authorizer() (autorest.Authorizer, error) {
 669  	if len(ccc.AuxTenants) == 0 {
 670  		spToken, err := ccc.ServicePrincipalToken()
 671  		if err != nil {
 672  			return nil, fmt.Errorf("failed to get oauth token from certificate auth: %v", err)
 673  		}
 674  		return autorest.NewBearerAuthorizer(spToken), nil
 675  	}
 676  	mtSPT, err := ccc.MultiTenantServicePrincipalToken()
 677  	if err != nil {
 678  		return nil, fmt.Errorf("failed to get multitenant SPT from certificate auth: %v", err)
 679  	}
 680  	return autorest.NewMultiTenantServicePrincipalTokenAuthorizer(mtSPT), nil
 681  }
 682  
 683  // DeviceFlowConfig provides the options to get a bearer authorizer using device flow authentication.
 684  type DeviceFlowConfig struct {
 685  	ClientID    string
 686  	TenantID    string
 687  	AADEndpoint string
 688  	Resource    string
 689  }
 690  
 691  // Authorizer gets the authorizer from device flow.
 692  func (dfc DeviceFlowConfig) Authorizer() (autorest.Authorizer, error) {
 693  	spToken, err := dfc.ServicePrincipalToken()
 694  	if err != nil {
 695  		return nil, fmt.Errorf("failed to get oauth token from device flow: %v", err)
 696  	}
 697  	return autorest.NewBearerAuthorizer(spToken), nil
 698  }
 699  
 700  // ServicePrincipalToken gets the service principal token from device flow.
 701  func (dfc DeviceFlowConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
 702  	oauthConfig, err := adal.NewOAuthConfig(dfc.AADEndpoint, dfc.TenantID)
 703  	if err != nil {
 704  		return nil, err
 705  	}
 706  	oauthClient := &autorest.Client{}
 707  	deviceCode, err := adal.InitiateDeviceAuth(oauthClient, *oauthConfig, dfc.ClientID, dfc.Resource)
 708  	if err != nil {
 709  		return nil, fmt.Errorf("failed to start device auth flow: %s", err)
 710  	}
 711  	log.Println(*deviceCode.Message)
 712  	token, err := adal.WaitForUserCompletion(oauthClient, deviceCode)
 713  	if err != nil {
 714  		return nil, fmt.Errorf("failed to finish device auth flow: %s", err)
 715  	}
 716  	return adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, dfc.ClientID, dfc.Resource, *token)
 717  }
 718  
 719  // UsernamePasswordConfig provides the options to get a bearer authorizer from a username and a password.
 720  type UsernamePasswordConfig struct {
 721  	ClientID    string
 722  	Username    string
 723  	Password    string
 724  	TenantID    string
 725  	AADEndpoint string
 726  	Resource    string
 727  }
 728  
 729  // ServicePrincipalToken creates a ServicePrincipalToken from username and password.
 730  func (ups UsernamePasswordConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
 731  	oauthConfig, err := adal.NewOAuthConfig(ups.AADEndpoint, ups.TenantID)
 732  	if err != nil {
 733  		return nil, err
 734  	}
 735  	return adal.NewServicePrincipalTokenFromUsernamePassword(*oauthConfig, ups.ClientID, ups.Username, ups.Password, ups.Resource)
 736  }
 737  
 738  // Authorizer gets the authorizer from a username and a password.
 739  func (ups UsernamePasswordConfig) Authorizer() (autorest.Authorizer, error) {
 740  	spToken, err := ups.ServicePrincipalToken()
 741  	if err != nil {
 742  		return nil, fmt.Errorf("failed to get oauth token from username and password auth: %v", err)
 743  	}
 744  	return autorest.NewBearerAuthorizer(spToken), nil
 745  }
 746  
 747  // MSIConfig provides the options to get a bearer authorizer through MSI.
 748  type MSIConfig struct {
 749  	Resource string
 750  	ClientID string
 751  }
 752  
 753  // ServicePrincipalToken creates a ServicePrincipalToken from MSI.
 754  func (mc MSIConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
 755  	spToken, err := adal.NewServicePrincipalTokenFromManagedIdentity(mc.Resource, &adal.ManagedIdentityOptions{
 756  		ClientID: mc.ClientID,
 757  	})
 758  	if err != nil {
 759  		return nil, fmt.Errorf("failed to get oauth token from MSI: %v", err)
 760  	}
 761  	return spToken, nil
 762  }
 763  
 764  // Authorizer gets the authorizer from MSI.
 765  func (mc MSIConfig) Authorizer() (autorest.Authorizer, error) {
 766  	spToken, err := mc.ServicePrincipalToken()
 767  	if err != nil {
 768  		return nil, err
 769  	}
 770  
 771  	return autorest.NewBearerAuthorizer(spToken), nil
 772  }
 773