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