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