...

Source file src/github.com/Azure/go-autorest/autorest/adal/cmd/adal.go

Documentation: github.com/Azure/go-autorest/autorest/adal/cmd

     1  package main
     2  
     3  // Copyright 2017 Microsoft Corporation
     4  //
     5  //  Licensed under the Apache License, Version 2.0 (the "License");
     6  //  you may not use this file except in compliance with the License.
     7  //  You may obtain a copy of the License at
     8  //
     9  //      http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  //  Unless required by applicable law or agreed to in writing, software
    12  //  distributed under the License is distributed on an "AS IS" BASIS,
    13  //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  //  See the License for the specific language governing permissions and
    15  //  limitations under the License.
    16  
    17  import (
    18  	"flag"
    19  	"fmt"
    20  	"log"
    21  	"strings"
    22  
    23  	"crypto/rsa"
    24  	"crypto/x509"
    25  	"io/ioutil"
    26  	"net/http"
    27  	"os/user"
    28  
    29  	"github.com/Azure/go-autorest/autorest/adal"
    30  )
    31  
    32  const (
    33  	deviceMode        = "device"
    34  	clientSecretMode  = "secret"
    35  	clientCertMode    = "cert"
    36  	refreshMode       = "refresh"
    37  	msiDefaultMode    = "msiDefault"
    38  	msiClientIDMode   = "msiClientID"
    39  	msiResourceIDMode = "msiResourceID"
    40  
    41  	activeDirectoryEndpoint = "https://login.microsoftonline.com/"
    42  )
    43  
    44  type option struct {
    45  	name  string
    46  	value string
    47  }
    48  
    49  var (
    50  	mode     string
    51  	resource string
    52  
    53  	tenantID           string
    54  	applicationID      string
    55  	identityResourceID string
    56  
    57  	applicationSecret string
    58  	certificatePath   string
    59  
    60  	tokenCachePath string
    61  )
    62  
    63  func checkMandatoryOptions(mode string, options ...option) {
    64  	for _, option := range options {
    65  		if strings.TrimSpace(option.value) == "" {
    66  			log.Fatalf("Authentication mode '%s' requires mandatory option '%s'.", mode, option.name)
    67  		}
    68  	}
    69  }
    70  
    71  func defaultTokenCachePath() string {
    72  	usr, err := user.Current()
    73  	if err != nil {
    74  		log.Fatal(err)
    75  	}
    76  	defaultTokenPath := usr.HomeDir + "/.adal/accessToken.json"
    77  	return defaultTokenPath
    78  }
    79  
    80  func init() {
    81  	flag.StringVar(&mode, "mode", "device", "authentication mode (device, secret, cert, refresh)")
    82  	flag.StringVar(&resource, "resource", "", "resource for which the token is requested")
    83  	flag.StringVar(&tenantID, "tenantId", "", "tenant id")
    84  	flag.StringVar(&applicationID, "applicationId", "", "application id")
    85  	flag.StringVar(&applicationSecret, "secret", "", "application secret")
    86  	flag.StringVar(&certificatePath, "certificatePath", "", "path to pk12/PFC application certificate")
    87  	flag.StringVar(&tokenCachePath, "tokenCachePath", defaultTokenCachePath(), "location of oath token cache")
    88  	flag.StringVar(&identityResourceID, "identityResourceID", "", "managedIdentity azure resource id")
    89  
    90  	flag.Parse()
    91  
    92  	switch mode = strings.TrimSpace(mode); mode {
    93  	case msiDefaultMode:
    94  		checkMandatoryOptions(msiDefaultMode,
    95  			option{name: "resource", value: resource},
    96  			option{name: "tenantId", value: tenantID},
    97  		)
    98  	case msiClientIDMode:
    99  		checkMandatoryOptions(msiClientIDMode,
   100  			option{name: "resource", value: resource},
   101  			option{name: "tenantId", value: tenantID},
   102  			option{name: "applicationId", value: applicationID},
   103  		)
   104  	case msiResourceIDMode:
   105  		checkMandatoryOptions(msiResourceIDMode,
   106  			option{name: "resource", value: resource},
   107  			option{name: "tenantId", value: tenantID},
   108  			option{name: "identityResourceID", value: identityResourceID},
   109  		)
   110  	case clientSecretMode:
   111  		checkMandatoryOptions(clientSecretMode,
   112  			option{name: "resource", value: resource},
   113  			option{name: "tenantId", value: tenantID},
   114  			option{name: "applicationId", value: applicationID},
   115  			option{name: "secret", value: applicationSecret},
   116  		)
   117  	case clientCertMode:
   118  		checkMandatoryOptions(clientCertMode,
   119  			option{name: "resource", value: resource},
   120  			option{name: "tenantId", value: tenantID},
   121  			option{name: "applicationId", value: applicationID},
   122  			option{name: "certificatePath", value: certificatePath},
   123  		)
   124  	case deviceMode:
   125  		checkMandatoryOptions(deviceMode,
   126  			option{name: "resource", value: resource},
   127  			option{name: "tenantId", value: tenantID},
   128  			option{name: "applicationId", value: applicationID},
   129  		)
   130  	case refreshMode:
   131  		checkMandatoryOptions(refreshMode,
   132  			option{name: "resource", value: resource},
   133  			option{name: "tenantId", value: tenantID},
   134  			option{name: "applicationId", value: applicationID},
   135  		)
   136  	default:
   137  		log.Fatalln("Authentication modes 'secret, 'cert', 'device' or 'refresh' are supported.")
   138  	}
   139  }
   140  
   141  func acquireTokenClientSecretFlow(oauthConfig adal.OAuthConfig,
   142  	appliationID string,
   143  	applicationSecret string,
   144  	resource string,
   145  	callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) {
   146  
   147  	spt, err := adal.NewServicePrincipalToken(
   148  		oauthConfig,
   149  		appliationID,
   150  		applicationSecret,
   151  		resource,
   152  		callbacks...)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	return spt, spt.Refresh()
   158  }
   159  
   160  func decodePkcs12(pkcs []byte, password string) (*x509.Certificate, *rsa.PrivateKey, error) {
   161  	return adal.DecodePfxCertificateData(pkcs, password)
   162  }
   163  
   164  func acquireTokenMSIFlow(applicationID string,
   165  	identityResourceID string,
   166  	resource string,
   167  	callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) {
   168  
   169  	// only one of them can be present:
   170  	if applicationID != "" && identityResourceID != "" {
   171  		return nil, fmt.Errorf("didn't expect applicationID and identityResourceID at same time")
   172  	}
   173  
   174  	msiEndpoint, _ := adal.GetMSIVMEndpoint()
   175  	var spt *adal.ServicePrincipalToken
   176  	var err error
   177  
   178  	// both can be empty, systemAssignedMSI scenario
   179  	if applicationID == "" && identityResourceID == "" {
   180  		spt, err = adal.NewServicePrincipalTokenFromMSI(msiEndpoint, resource, callbacks...)
   181  	}
   182  
   183  	// msi login with clientID
   184  	if applicationID != "" {
   185  		spt, err = adal.NewServicePrincipalTokenFromMSIWithUserAssignedID(msiEndpoint, resource, applicationID, callbacks...)
   186  	}
   187  
   188  	// msi login with resourceID
   189  	if identityResourceID != "" {
   190  		spt, err = adal.NewServicePrincipalTokenFromMSIWithIdentityResourceID(msiEndpoint, resource, identityResourceID, callbacks...)
   191  	}
   192  
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  
   197  	return spt, spt.Refresh()
   198  }
   199  
   200  func acquireTokenClientCertFlow(oauthConfig adal.OAuthConfig,
   201  	applicationID string,
   202  	applicationCertPath string,
   203  	resource string,
   204  	callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) {
   205  
   206  	certData, err := ioutil.ReadFile(certificatePath)
   207  	if err != nil {
   208  		return nil, fmt.Errorf("failed to read the certificate file (%s): %v", certificatePath, err)
   209  	}
   210  
   211  	certificate, rsaPrivateKey, err := decodePkcs12(certData, "")
   212  	if err != nil {
   213  		return nil, fmt.Errorf("failed to decode pkcs12 certificate while creating spt: %v", err)
   214  	}
   215  
   216  	spt, err := adal.NewServicePrincipalTokenFromCertificate(
   217  		oauthConfig,
   218  		applicationID,
   219  		certificate,
   220  		rsaPrivateKey,
   221  		resource,
   222  		callbacks...)
   223  	if err != nil {
   224  		return nil, err
   225  	}
   226  
   227  	return spt, spt.Refresh()
   228  }
   229  
   230  func acquireTokenDeviceCodeFlow(oauthConfig adal.OAuthConfig,
   231  	applicationID string,
   232  	resource string,
   233  	callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) {
   234  
   235  	oauthClient := &http.Client{}
   236  	deviceCode, err := adal.InitiateDeviceAuth(
   237  		oauthClient,
   238  		oauthConfig,
   239  		applicationID,
   240  		resource)
   241  	if err != nil {
   242  		return nil, fmt.Errorf("Failed to start device auth flow: %s", err)
   243  	}
   244  
   245  	fmt.Println(*deviceCode.Message)
   246  
   247  	token, err := adal.WaitForUserCompletion(oauthClient, deviceCode)
   248  	if err != nil {
   249  		return nil, fmt.Errorf("Failed to finish device auth flow: %s", err)
   250  	}
   251  
   252  	spt, err := adal.NewServicePrincipalTokenFromManualToken(
   253  		oauthConfig,
   254  		applicationID,
   255  		resource,
   256  		*token,
   257  		callbacks...)
   258  	return spt, err
   259  }
   260  
   261  func refreshToken(oauthConfig adal.OAuthConfig,
   262  	applicationID string,
   263  	resource string,
   264  	tokenCachePath string,
   265  	callbacks ...adal.TokenRefreshCallback) (*adal.ServicePrincipalToken, error) {
   266  
   267  	token, err := adal.LoadToken(tokenCachePath)
   268  	if err != nil {
   269  		return nil, fmt.Errorf("failed to load token from cache: %v", err)
   270  	}
   271  
   272  	spt, err := adal.NewServicePrincipalTokenFromManualToken(
   273  		oauthConfig,
   274  		applicationID,
   275  		resource,
   276  		*token,
   277  		callbacks...)
   278  	if err != nil {
   279  		return nil, err
   280  	}
   281  	return spt, spt.Refresh()
   282  }
   283  
   284  func saveToken(spt adal.Token) error {
   285  	if tokenCachePath != "" {
   286  		err := adal.SaveToken(tokenCachePath, 0600, spt)
   287  		if err != nil {
   288  			return err
   289  		}
   290  		log.Printf("Acquired token was saved in '%s' file\n", tokenCachePath)
   291  		return nil
   292  
   293  	}
   294  	return fmt.Errorf("empty path for token cache")
   295  }
   296  
   297  func main() {
   298  	oauthConfig, err := adal.NewOAuthConfig(activeDirectoryEndpoint, tenantID)
   299  	if err != nil {
   300  		panic(err)
   301  	}
   302  
   303  	callback := func(token adal.Token) error {
   304  		return saveToken(token)
   305  	}
   306  
   307  	log.Printf("Authenticating with mode '%s'\n", mode)
   308  	switch mode {
   309  	case clientSecretMode:
   310  		_, err = acquireTokenClientSecretFlow(
   311  			*oauthConfig,
   312  			applicationID,
   313  			applicationSecret,
   314  			resource,
   315  			callback)
   316  	case clientCertMode:
   317  		_, err = acquireTokenClientCertFlow(
   318  			*oauthConfig,
   319  			applicationID,
   320  			certificatePath,
   321  			resource,
   322  			callback)
   323  	case deviceMode:
   324  		var spt *adal.ServicePrincipalToken
   325  		spt, err = acquireTokenDeviceCodeFlow(
   326  			*oauthConfig,
   327  			applicationID,
   328  			resource,
   329  			callback)
   330  		if err == nil {
   331  			err = saveToken(spt.Token())
   332  		}
   333  	case msiResourceIDMode:
   334  		fallthrough
   335  	case msiClientIDMode:
   336  		fallthrough
   337  	case msiDefaultMode:
   338  		var spt *adal.ServicePrincipalToken
   339  		spt, err = acquireTokenMSIFlow(
   340  			applicationID,
   341  			identityResourceID,
   342  			resource,
   343  			callback)
   344  		if err == nil {
   345  			err = saveToken(spt.Token())
   346  		}
   347  	case refreshMode:
   348  		_, err = refreshToken(
   349  			*oauthConfig,
   350  			applicationID,
   351  			resource,
   352  			tokenCachePath,
   353  			callback)
   354  	}
   355  
   356  	if err != nil {
   357  		log.Fatalf("Failed to acquire a token for resource %s. Error: %v", resource, err)
   358  	}
   359  }
   360  

View as plain text