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	spToken, err := adal.NewServicePrincipalTokenFromManagedIdentity(mc.Resource, &adal.ManagedIdentityOptions{
741		ClientID: mc.ClientID,
742	})
743	if err != nil {
744		return nil, fmt.Errorf("failed to get oauth token from MSI: %v", err)
745	}
746	return spToken, nil
747}
748
749// Authorizer gets the authorizer from MSI.
750func (mc MSIConfig) Authorizer() (autorest.Authorizer, error) {
751	spToken, err := mc.ServicePrincipalToken()
752	if err != nil {
753		return nil, err
754	}
755
756	return autorest.NewBearerAuthorizer(spToken), nil
757}
758