// tokenFromDeviceFlow prints a message to the screen for user to take action to // consent application on a browser and in the meanwhile the authentication // endpoint is polled until user gives consent, denies or the flow times out. // Returned token must be saved. func tokenFromDeviceFlow(oauthCfg azure.OAuthConfig, tokenPath, clientID, resource string) (*azure.ServicePrincipalToken, error) { cl := oauthClient() deviceCode, err := azure.InitiateDeviceAuth(&cl, oauthCfg, clientID, resource) if err != nil { return nil, fmt.Errorf("Failed to start device auth: %v", err) } log.Debug("Retrieved device code.", logutil.Fields{ "expires_in": to.Int64(deviceCode.ExpiresIn), "interval": to.Int64(deviceCode.Interval), }) // Example message: “To sign in, open https://aka.ms/devicelogin and enter // the code 0000000 to authenticate.” log.Infof("Microsoft Azure: %s", to.String(deviceCode.Message)) token, err := azure.WaitForUserCompletion(&cl, deviceCode) if err != nil { return nil, fmt.Errorf("Failed to complete device auth: %v", err) } spt, err := azure.NewServicePrincipalTokenFromManualToken(oauthCfg, clientID, resource, *token) if err != nil { return nil, fmt.Errorf("Error constructing service principal token: %v", err) } return spt, nil }
func getSptFromDeviceFlow(oauthConfig azure.OAuthConfig, clientID, resource string, callbacks ...azure.TokenRefreshCallback) (*azure.ServicePrincipalToken, error) { oauthClient := &autorest.Client{} deviceCode, err := azure.InitiateDeviceAuth(oauthClient, oauthConfig, clientID, resource) if err != nil { return nil, fmt.Errorf("failed to start device auth flow: %s", err) } fmt.Println(*deviceCode.Message) token, err := azure.WaitForUserCompletion(oauthClient, deviceCode) if err != nil { return nil, fmt.Errorf("failed to finish device auth flow: %s", err) } spt, err := azure.NewServicePrincipalTokenFromManualToken( oauthConfig, clientID, resource, *token, callbacks...) if err != nil { return nil, fmt.Errorf("failed to get oauth token from device flow: %v", err) } return spt, nil }
func getSptFromCachedToken(oauthConfig azure.OAuthConfig, clientID, resource string, callbacks ...azure.TokenRefreshCallback) (*azure.ServicePrincipalToken, error) { token, err := azure.LoadToken(tokenCachePath) if err != nil { return nil, fmt.Errorf("failed to load token from cache: %v", err) } spt, _ := azure.NewServicePrincipalTokenFromManualToken( oauthConfig, clientID, resource, *token, callbacks...) return spt, nil }
// tokenFromFile returns a token from the specified file if it is found, otherwise // returns nil. Any error retrieving or creating the token is returned as an error. func tokenFromFile(say func(string), oauthCfg azure.OAuthConfig, tokenPath, clientID, resource string, callback azure.TokenRefreshCallback) (*azure.ServicePrincipalToken, error) { say(fmt.Sprintf("Loading auth token from file: %s", tokenPath)) if _, err := os.Stat(tokenPath); err != nil { if os.IsNotExist(err) { // file not found return nil, nil } return nil, err } token, err := azure.LoadToken(tokenPath) if err != nil { return nil, fmt.Errorf("Failed to load token from file: %v", err) } spt, err := azure.NewServicePrincipalTokenFromManualToken(oauthCfg, clientID, resource, *token, callback) if err != nil { return nil, fmt.Errorf("Error constructing service principal token: %v", err) } return spt, nil }
// tokenFromDeviceFlow prints a message to the screen for user to take action to // consent application on a browser and in the meanwhile the authentication // endpoint is polled until user gives consent, denies or the flow times out. // Returned token must be saved. func tokenFromDeviceFlow(say func(string), oauthCfg azure.OAuthConfig, tokenPath, clientID, resource string) (*azure.ServicePrincipalToken, error) { cl := autorest.NewClientWithUserAgent(userAgent) deviceCode, err := azure.InitiateDeviceAuth(&cl, oauthCfg, clientID, resource) if err != nil { return nil, fmt.Errorf("Failed to start device auth: %v", err) } // Example message: “To sign in, open https://aka.ms/devicelogin and enter // the code 0000000 to authenticate.” say(fmt.Sprintf("Microsoft Azure: %s", to.String(deviceCode.Message))) token, err := azure.WaitForUserCompletion(&cl, deviceCode) if err != nil { return nil, fmt.Errorf("Failed to complete device auth: %v", err) } spt, err := azure.NewServicePrincipalTokenFromManualToken(oauthCfg, clientID, resource, *token) if err != nil { return nil, fmt.Errorf("Error constructing service principal token: %v", err) } return spt, nil }
// InteractiveCreateServicePrincipal interactively creates service // principals for a subscription. func InteractiveCreateServicePrincipal( stderr io.Writer, sender autorest.Sender, requestInspector autorest.PrepareDecorator, resourceManagerEndpoint string, graphEndpoint string, subscriptionId string, clock clock.Clock, newUUID func() (utils.UUID, error), ) (appId, password string, _ error) { subscriptionsClient := subscriptions.Client{ subscriptions.NewWithBaseURI(resourceManagerEndpoint), } subscriptionsClient.Sender = sender setClientInspectors(&subscriptionsClient.Client, requestInspector, "azure.subscriptions") oauthConfig, tenantId, err := OAuthConfig( subscriptionsClient, resourceManagerEndpoint, subscriptionId, ) if err != nil { return "", "", errors.Trace(err) } client := autorest.NewClientWithUserAgent("juju") client.Sender = sender setClientInspectors(&client, requestInspector, "azure.autorest") // Perform the interactive authentication. The user will be prompted to // open a URL and input a device code, after which they will have to // enter their username and password if they are not already // authenticated with Azure. fmt.Fprintln(stderr, "Initiating interactive authentication.") fmt.Fprintln(stderr) armResource := TokenResource(resourceManagerEndpoint) clientId := jujuApplicationId deviceCode, err := azure.InitiateDeviceAuth(&client, *oauthConfig, clientId, armResource) if err != nil { return "", "", errors.Annotate(err, "initiating interactive authentication") } fmt.Fprintln(stderr, to.String(deviceCode.Message)+"\n") token, err := azure.WaitForUserCompletion(&client, deviceCode) if err != nil { return "", "", errors.Annotate(err, "waiting for interactive authentication to completed") } // Create service principal tokens that we can use to authorize API // requests to Active Directory and Resource Manager. These tokens // are only valid for a short amount of time, so we must create a // service principal password that can be used to obtain new tokens. armSpt, err := azure.NewServicePrincipalTokenFromManualToken(*oauthConfig, clientId, armResource, *token) if err != nil { return "", "", errors.Annotate(err, "creating temporary ARM service principal token") } if client.Sender != nil { armSpt.SetSender(client.Sender) } if err := armSpt.Refresh(); err != nil { return "", "", errors.Trace(err) } // The application requires permissions for both ARM and AD, so we // can use the token for both APIs. graphResource := TokenResource(graphEndpoint) graphToken := armSpt.Token graphToken.Resource = graphResource graphSpt, err := azure.NewServicePrincipalTokenFromManualToken(*oauthConfig, clientId, graphResource, graphToken) if err != nil { return "", "", errors.Annotate(err, "creating temporary Graph service principal token") } if client.Sender != nil { graphSpt.SetSender(client.Sender) } if err := graphSpt.Refresh(); err != nil { return "", "", errors.Trace(err) } directoryURL, err := url.Parse(graphEndpoint) if err != nil { return "", "", errors.Annotate(err, "parsing identity endpoint") } directoryURL.Path = path.Join(directoryURL.Path, tenantId) directoryClient := ad.NewManagementClient(directoryURL.String()) authorizationClient := authorization.NewWithBaseURI(resourceManagerEndpoint, subscriptionId) directoryClient.Authorizer = graphSpt authorizationClient.Authorizer = armSpt authorizationClient.Sender = client.Sender directoryClient.Sender = client.Sender setClientInspectors(&directoryClient.Client, requestInspector, "azure.directory") setClientInspectors(&authorizationClient.Client, requestInspector, "azure.authorization") userObject, err := ad.UsersClient{directoryClient}.GetCurrentUser() if err != nil { return "", "", errors.Trace(err) } fmt.Fprintf(stderr, "Authenticated as %q.\n", userObject.DisplayName) fmt.Fprintln(stderr, "Creating/updating service principal.") servicePrincipalObjectId, password, err := createOrUpdateServicePrincipal( ad.ServicePrincipalsClient{directoryClient}, subscriptionId, clock, newUUID, ) if err != nil { return "", "", errors.Trace(err) } fmt.Fprintln(stderr, "Assigning Owner role to service principal.") if err := createRoleAssignment( authorizationClient, subscriptionId, servicePrincipalObjectId, newUUID, ); err != nil { return "", "", errors.Trace(err) } return jujuApplicationId, password, nil }