Example #1
0
File: oauth.go Project: bac/juju
// OAuthConfig returns an azure.OAuthConfig based on the given resource
// manager endpoint and subscription ID. This will make a request to the
// resource manager API to discover the Active Directory tenant ID.
func OAuthConfig(
	client subscriptions.Client,
	resourceManagerEndpoint string,
	subscriptionId string,
) (*azure.OAuthConfig, string, error) {
	authURI, err := DiscoverAuthorizationURI(client, subscriptionId)
	if err != nil {
		return nil, "", errors.Annotate(err, "detecting auth URI")
	}
	logger.Debugf("discovered auth URI: %s", authURI)

	// The authorization URI scheme and host identifies the AD endpoint.
	// The authorization URI path identifies the AD tenant.
	tenantId, err := AuthorizationURITenantID(authURI)
	if err != nil {
		return nil, "", errors.Annotate(err, "getting tenant ID")
	}
	authURI.Path = ""
	adEndpoint := authURI.String()

	cloudEnv := azure.Environment{ActiveDirectoryEndpoint: adEndpoint}
	oauthConfig, err := cloudEnv.OAuthConfigForTenant(tenantId)
	if err != nil {
		return nil, "", errors.Annotate(err, "getting OAuth configuration")
	}
	return oauthConfig, tenantId, nil
}
Example #2
0
// AuthenticateServicePrincipal uses given service principal credentials to return a
// service principal token. Generated token is not stored in a cache file or refreshed.
func AuthenticateServicePrincipal(env azure.Environment, subscriptionID, spID, spPassword string) (*azure.ServicePrincipalToken, error) {
	tenantID, err := loadOrFindTenantID(env, subscriptionID)
	if err != nil {
		return nil, err
	}
	oauthCfg, err := env.OAuthConfigForTenant(tenantID)
	if err != nil {
		return nil, fmt.Errorf("Failed to obtain oauth config for azure environment: %v", err)
	}

	spt, err := azure.NewServicePrincipalToken(*oauthCfg, spID, spPassword, getScope(env))
	if err != nil {
		return nil, fmt.Errorf("Failed to create service principal token: %+v", err)
	}
	return spt, nil
}
Example #3
0
// Authenticate fetches a token from the local file cache or initiates a consent
// flow and waits for token to be obtained.
func Authenticate(env azure.Environment, subscriptionID, tenantID string, say func(string)) (*azure.ServicePrincipalToken, error) {
	clientID, ok := clientIDs[env.Name]
	if !ok {
		return nil, fmt.Errorf("packer-azure application not set up for Azure environment %q", env.Name)
	}

	oauthCfg, err := env.OAuthConfigForTenant(tenantID)
	if err != nil {
		return nil, fmt.Errorf("Failed to obtain oauth config for azure environment: %v", err)
	}

	// for AzurePublicCloud (https://management.core.windows.net/), this old
	// Service Management scope covers both ASM and ARM.
	apiScope := env.ServiceManagementEndpoint

	tokenPath := tokenCachePath(tenantID)
	saveToken := mkTokenCallback(tokenPath)
	saveTokenCallback := func(t azure.Token) error {
		say("Azure token expired. Saving the refreshed token...")
		return saveToken(t)
	}

	// Lookup the token cache file for an existing token.
	spt, err := tokenFromFile(say, *oauthCfg, tokenPath, clientID, apiScope, saveTokenCallback)
	if err != nil {
		return nil, err
	}
	if spt != nil {
		say(fmt.Sprintf("Auth token found in file: %s", tokenPath))

		// NOTE(ahmetalpbalkan): The token file we found may contain an
		// expired access_token. In that case, the first call to Azure SDK will
		// attempt to refresh the token using refresh_token, which might have
		// expired[1], in that case we will get an error and we shall remove the
		// token file and initiate token flow again so that the user would not
		// need removing the token cache file manually.
		//
		// [1]: expiration date of refresh_token is not returned in AAD /token
		//      response, we just know it is 14 days. Therefore user’s token
		//      will go stale every 14 days and we will delete the token file,
		//      re-initiate the device flow.
		say("Validating the token.")
		if err := validateToken(env, spt); err != nil {
			say(fmt.Sprintf("Error: %v", err))
			say("Stored Azure credentials expired. Please reauthenticate.")
			say(fmt.Sprintf("Deleting %s", tokenPath))
			if err := os.RemoveAll(tokenPath); err != nil {
				return nil, fmt.Errorf("Error deleting stale token file: %v", err)
			}
		} else {
			say("Token works.")
			return spt, nil
		}
	}

	// Start an OAuth 2.0 device flow
	say(fmt.Sprintf("Initiating device flow: %s", tokenPath))
	spt, err = tokenFromDeviceFlow(say, *oauthCfg, tokenPath, clientID, apiScope)
	if err != nil {
		return nil, err
	}
	say("Obtained service principal token.")
	if err := saveToken(spt.Token); err != nil {
		say("Error occurred saving token to cache file.")
		return nil, err
	}
	return spt, nil
}
Example #4
0
// AuthenticateDeviceFlow fetches a token from the local file cache or initiates a consent
// flow and waits for token to be obtained. Obtained token is stored in a file cache for
// future use and refreshing.
func AuthenticateDeviceFlow(env azure.Environment, subscriptionID string) (*azure.ServicePrincipalToken, error) {
	// First we locate the tenant ID of the subscription as we store tokens per
	// tenant (which could have multiple subscriptions)
	tenantID, err := loadOrFindTenantID(env, subscriptionID)
	if err != nil {
		return nil, err
	}
	oauthCfg, err := env.OAuthConfigForTenant(tenantID)
	if err != nil {
		return nil, fmt.Errorf("Failed to obtain oauth config for azure environment: %v", err)
	}

	tokenPath := tokenCachePath(tenantID)
	saveToken := mkTokenCallback(tokenPath)
	saveTokenCallback := func(t azure.Token) error {
		log.Debug("Azure token expired. Saving the refreshed token...")
		return saveToken(t)
	}
	f := logutil.Fields{"path": tokenPath}

	appID, ok := appIDs[env.Name]
	if !ok {
		return nil, fmt.Errorf("docker-machine application not set up for Azure environment %q", env.Name)
	}
	scope := getScope(env)

	// Lookup the token cache file for an existing token.
	spt, err := tokenFromFile(*oauthCfg, tokenPath, appID, scope, saveTokenCallback)
	if err != nil {
		return nil, err
	}
	if spt != nil {
		log.Debug("Auth token found in file.", f)

		// NOTE(ahmetalpbalkan): The token file we found might be containing an
		// expired access_token. In that case, the first call to Azure SDK will
		// attempt to refresh the token using refresh_token –which might have
		// expired[1], in that case we will get an error and we shall remove the
		// token file and initiate token flow again so that the user would not
		// need removing the token cache file manually.
		//
		// [1]: for device flow auth, the expiration date of refresh_token is
		//      not returned in AAD /token response, we just know it is 14
		//      days. Therefore user’s token will go stale every 14 days and we
		//      will delete the token file, re-initiate the device flow. Service
		//      Principal Account tokens are not subject to this limitation.
		log.Debug("Validating the token.")
		if err := validateToken(env, spt); err != nil {
			log.Debug(fmt.Sprintf("Error: %v", err))
			log.Debug(fmt.Sprintf("Deleting %s", tokenPath))
			if err := os.RemoveAll(tokenPath); err != nil {
				return nil, fmt.Errorf("Error deleting stale token file: %v", err)
			}
		} else {
			log.Debug("Token works.")
			return spt, nil
		}
	}

	log.Debug("Obtaining a token.", f)
	spt, err = deviceFlowAuth(*oauthCfg, appID, scope)
	if err != nil {
		return nil, err
	}
	log.Debug("Obtained a token.")
	if err := saveToken(spt.Token); err != nil {
		log.Error("Error occurred saving token to cache file.")
		return nil, err
	}
	return spt, nil
}