# File lib/fog/openstack.rb, line 266
    def self.authenticate_v3(options, connection_options = {})
      uri                   = options[:openstack_auth_uri]
      project_name          = options[:openstack_project_name]
      service_type          = options[:openstack_service_type]
      service_name          = options[:openstack_service_name]
      identity_service_type = options[:openstack_identity_service_type]
      endpoint_type         = map_endpoint_type(options[:openstack_endpoint_type] || 'publicURL')
      openstack_region      = options[:openstack_region]

      token, body = retrieve_tokens_v3 options, connection_options

      service = get_service_v3(body, service_type, service_name, openstack_region, options)

      options[:unscoped_token] = token

      unless service
        unless project_name
          request_body = {
              :expects => [200],
              :headers => {'Content-Type' => 'application/json',
                           'Accept' => 'application/json',
                           'X-Auth-Token' => token},
              :method => 'GET'
          }
          user_id = body['token']['user']['id']
          project_uri = uri.clone
          project_uri.path = uri.path.sub('/auth/tokens', "/users/#{user_id}/projects")
          project_uri_param = "#{project_uri.scheme}://#{project_uri.host}:#{project_uri.port}#{project_uri.path}"
          response = Fog::Core::Connection.new(project_uri_param, false, connection_options).request(request_body)

          projects_body = Fog::JSON.decode(response.body)
          if projects_body['projects'].empty?
            options[:openstack_domain_id] = body['token']['user']['domain']['id']
          else
            options[:openstack_project_id] = projects_body['projects'].first['id']
            options[:openstack_project_name] = projects_body['projects'].first['name']
            options[:openstack_domain_id] = projects_body['projects'].first['domain_id']
          end
        end

        token, body = retrieve_tokens_v3(options, connection_options)
        service = get_service_v3(body, service_type, service_name, openstack_region, options)
      end

      # If no identity endpoint is found, try the auth uri (slicing /auth/tokens)
      # It covers the case where identityv3 endpoint is not present in the catalog but we have to use it
      unless service
        if service_type.include? "identity"
          identity_uri = uri.to_s.sub('/auth/tokens', '')
          response = Fog::Core::Connection.new(identity_uri, false, connection_options).request({:method => 'GET'})
          if response.status == 200
            service = {
              "endpoints" => [{
                 "url"       => identity_uri,
                 "region"    => openstack_region,
                 "interface" => endpoint_type
              }],
              "type"      => service_type,
              "name"      => service_name
            }
          end
        end
      end

      unless service
        available_services = body['token']['catalog'].map { |service|
          service['type']
        }.sort.join ', '

        available_regions = body['token']['catalog'].map { |service|
          service['endpoints'].map { |endpoint|
            endpoint['region']
          }.uniq
        }.uniq.sort.join ', '

        missing = service_type.join ', '

        message = "Could not find service #{missing}#{(' in region '+openstack_region) if openstack_region}."+
            " Have #{available_services}#{(' in regions '+available_regions) if openstack_region}"

        raise Fog::Errors::NotFound, message
      end

      service['endpoints'] = service['endpoints'].select do |endpoint|
        endpoint['region'] == openstack_region && endpoint['interface'] == endpoint_type
      end if openstack_region

      if service['endpoints'].empty?
        raise Fog::Errors::NotFound.new("No endpoints available for region '#{openstack_region}'")
      end if openstack_region

      regions = service["endpoints"].map { |e| e['region'] }.uniq
      if regions.count > 1
        raise Fog::Errors::NotFound.new("Multiple regions available choose one of these '#{regions.join(',')}'")
      end

      identity_service = get_service_v3(body, identity_service_type, nil, nil, :openstack_endpoint_path_matches => /\/v3/) if identity_service_type

      management_url = service['endpoints'].find { |e| e['interface']==endpoint_type }['url']
      identity_url = identity_service['endpoints'].find { |e| e['interface']=='public' }['url'] if identity_service

      if body['token']['project']
        tenant = body['token']['project']
      elsif body['token']['user']['project']
        tenant = body['token']['user']['project']
      end

      return {
          :user                     => body['token']['user']['name'],
          :tenant                   => tenant,
          :identity_public_endpoint => identity_url,
          :server_management_url    => management_url,
          :token                    => token,
          :expires                  => body['token']['expires_at'],
          :current_user_id          => body['token']['user']['id'],
          :unscoped_token           => options[:unscoped_token]
      }
    end