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