diff --git a/Changelog.md b/Changelog.md
index 64e6494cda42f6524343777bb91facd4528f543c..be126faecce6d4c873e3aab661786c0ceae766e8 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -74,6 +74,7 @@ With the port to Bootstrap 3, app/views/terms/default.haml has a new structure.
 * Extract StatusMessageService from StatusMessagesController [#6280](https://github.com/diaspora/diaspora/pull/6280)
 * Refactor HomeController#toggle\_mobile [#6260](https://github.com/diaspora/diaspora/pull/6260)
 * Extract CommentService from CommentsController [#6307](https://github.com/diaspora/diaspora/pull/6307)
+* Extract user/profile discovery into the diaspora\_federation-rails gem [#6310](https://github.com/diaspora/diaspora/pull/6310)
 
 ## Bug fixes
 * Fix indentation and a link title on the default home page [#6212](https://github.com/diaspora/diaspora/pull/6212)
diff --git a/Gemfile b/Gemfile
index 4b4bb3b016b0317cae94ba8abf34475810f028ad..ee5e9bf203a584bd55fa23ef9e4453fc5f068da1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -12,7 +12,7 @@ gem "unicorn", "4.9.0", require: false
 
 # Federation
 
-gem "diaspora_federation-rails", "0.0.3"
+gem "diaspora_federation-rails", "0.0.6"
 
 # API and JSON
 
diff --git a/Gemfile.lock b/Gemfile.lock
index f7e89f649d49f83a58e02e1d5468ad6e08cede7d..e1c51db28113a16bfbac15b3659f4cb4b1f4ad8a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -154,10 +154,14 @@ GEM
       eventmachine (>= 1.0.5, < 1.1)
       http_parser.rb (~> 0.6)
       nokogiri (~> 1.6)
-    diaspora_federation (0.0.3)
+    diaspora_federation (0.0.6)
+      faraday (~> 0.9.0)
+      faraday_middleware (~> 0.10.0)
       nokogiri (~> 1.6, >= 1.6.6)
-    diaspora_federation-rails (0.0.3)
-      diaspora_federation (= 0.0.3)
+      typhoeus (~> 0.7.0)
+      valid (~> 1.0.0)
+    diaspora_federation-rails (0.0.6)
+      diaspora_federation (= 0.0.6)
       rails (~> 4.2)
     diff-lcs (1.2.5)
     docile (1.1.5)
@@ -755,6 +759,7 @@ GEM
       raindrops (~> 0.7)
     uuid (2.3.8)
       macaddr (~> 1.0)
+    valid (1.0.0)
     warden (1.2.3)
       rack (>= 1.0)
     webmock (1.21.0)
@@ -789,7 +794,7 @@ DEPENDENCIES
   devise-token_authenticatable (~> 0.4.0)
   devise_lastseenable (= 0.0.6)
   diaspora-vines (~> 0.1.28)
-  diaspora_federation-rails (= 0.0.3)
+  diaspora_federation-rails (= 0.0.6)
   entypo-rails (= 3.0.0.pre.rc2)
   eye (= 0.7.pre)
   factory_girl_rails (= 4.5.0)
diff --git a/app/controllers/people_controller.rb b/app/controllers/people_controller.rb
index a79a1df1edb517b0b57b39f76d37809c624484fa..d8cfbcde91d706ad72d86d113cfea795a9979236 100644
--- a/app/controllers/people_controller.rb
+++ b/app/controllers/people_controller.rb
@@ -41,7 +41,7 @@ class PeopleController < ApplicationController
         if diaspora_id?(search_query)
           @people =  Person.where(:diaspora_handle => search_query.downcase)
           if @people.empty?
-            Webfinger.in_background(search_query)
+            Workers::FetchWebfinger.perform_async(search_query)
             @background_query = search_query.downcase
           end
         end
@@ -127,7 +127,7 @@ class PeopleController < ApplicationController
 
   def retrieve_remote
     if params[:diaspora_handle]
-      Webfinger.in_background(params[:diaspora_handle], :single_aspect_form => true)
+      Workers::FetchWebfinger.perform_async(params[:diaspora_handle])
       render :nothing => true
     else
       render :nothing => true, :status => 422
diff --git a/app/models/comment.rb b/app/models/comment.rb
index f6cd6f3532d5c77d5cb3387305596b4c83bf2843..9e0eca2c74804fbd65b9fcea46e21fe90fce19bc 100644
--- a/app/models/comment.rb
+++ b/app/models/comment.rb
@@ -56,7 +56,7 @@ class Comment < ActiveRecord::Base
   end
 
   def diaspora_handle= nh
-    self.author = Webfinger.new(nh).fetch
+    self.author = Person.find_or_fetch_by_identifier(nh)
   end
 
   def notification_type(user, person)
diff --git a/app/models/conversation.rb b/app/models/conversation.rb
index 45777b82a163d8eb20551e10eab9843bd32db284..afd4d798c0283467e1d7cc2e7c016ef8d610f381 100644
--- a/app/models/conversation.rb
+++ b/app/models/conversation.rb
@@ -43,7 +43,7 @@ class Conversation < ActiveRecord::Base
   end
 
   def diaspora_handle= nh
-    self.author = Webfinger.new(nh).fetch
+    self.author = Person.find_or_fetch_by_identifier(nh)
   end
 
   def first_unread_message(user)
@@ -68,7 +68,7 @@ class Conversation < ActiveRecord::Base
   end
   def participant_handles= handles
     handles.split(';').each do |handle|
-      self.participants << Webfinger.new(handle).fetch
+      participants << Person.find_or_fetch_by_identifier(handle)
     end
   end
 
diff --git a/app/models/message.rb b/app/models/message.rb
index f73fc87b9fde24a223f8968c3d88d7801c7339df..91ef0405792c08cdba60a3410d5996cf696d1308 100644
--- a/app/models/message.rb
+++ b/app/models/message.rb
@@ -35,7 +35,7 @@ class Message < ActiveRecord::Base
   end
 
   def diaspora_handle= nh
-    self.author = Webfinger.new(nh).fetch
+    self.author = Person.find_or_fetch_by_identifier(nh)
   end
 
   def conversation_guid
diff --git a/app/models/person.rb b/app/models/person.rb
index 511bc47285eb7e9c4d854d03de11c3a5f322a01e..052ee76d3363f9fa342f2434784b7a08c4ffedd3 100644
--- a/app/models/person.rb
+++ b/app/models/person.rb
@@ -238,6 +238,19 @@ class Person < ActiveRecord::Base
     serialized_public_key = new_key
   end
 
+  # discovery (webfinger)
+  def self.find_or_fetch_by_identifier(account)
+    # exiting person?
+    person = by_account_identifier(account)
+    return person if person.present? && person.profile.present?
+
+    # create or update person from webfinger
+    logger.info "webfingering #{account}, it is not known or needs updating"
+    DiasporaFederation::Discovery::Discovery.new(account).fetch_and_save
+
+    by_account_identifier(account)
+  end
+
   # database calls
   def self.by_account_identifier(identifier)
     identifier = identifier.strip.downcase.sub("acct:", "")
@@ -252,32 +265,6 @@ class Person < ActiveRecord::Base
     where(guid: guid, closed_account: false).where.not(owner: nil).take
   end
 
-  def self.create_from_webfinger(profile, hcard)
-    return nil if profile.nil? || !profile.valid_diaspora_profile?
-    new_person = Person.new
-    new_person.serialized_public_key = profile.public_key
-    new_person.guid = profile.guid
-    new_person.diaspora_handle = profile.account
-    new_person.url = profile.seed_location
-
-    #hcard_profile = HCard.find profile.hcard.first[:href]
-    ::Logging::Logger[self].info "event=webfinger_marshal valid=#{new_person.valid?} " \
-                                 "target=#{new_person.diaspora_handle}"
-    new_person.assign_new_profile_from_hcard(hcard)
-    new_person.save!
-    new_person.profile.save!
-    new_person
-  end
-
-  def assign_new_profile_from_hcard(hcard)
-    self.profile = Profile.new(:first_name => hcard[:given_name],
-                              :last_name  => hcard[:family_name],
-                              :image_url  => hcard[:photo],
-                              :image_url_medium  => hcard[:photo_medium],
-                              :image_url_small  => hcard[:photo_small],
-                              :searchable => hcard[:searchable])
-  end
-
   def remote?
     owner_id.nil?
   end
@@ -359,7 +346,8 @@ class Person < ActiveRecord::Base
   end
 
   def fix_profile
-    Webfinger.new(self.diaspora_handle).fetch
-    self.reload
+    logger.info "fix profile for account: #{diaspora_handle}"
+    DiasporaFederation::Discovery::Discovery.new(diaspora_handle).fetch_and_save
+    reload
   end
 end
diff --git a/app/models/poll_participation.rb b/app/models/poll_participation.rb
index 23bd6dd7806313e354713ab9cc2affee94bd3353..64dd32ac519a3b563985c882d576887cf2198c1f 100644
--- a/app/models/poll_participation.rb
+++ b/app/models/poll_participation.rb
@@ -1,7 +1,7 @@
 class PollParticipation < ActiveRecord::Base
 
   include Diaspora::Federated::Base
-  
+
   include Diaspora::Guid
   include Diaspora::Relayable
   belongs_to :poll
@@ -37,7 +37,7 @@ class PollParticipation < ActiveRecord::Base
   end
 
   def diaspora_handle= nh
-    self.author = Webfinger.new(nh).fetch
+    self.author = Person.find_or_fetch_by_identifier(nh)
   end
 
   def not_already_participated
diff --git a/app/models/user.rb b/app/models/user.rb
index dbc587f911edd72127d07b40cd31305eae2ee5b9..2e39dec9dafefda508ae18a6bb64a33bbe323659 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -462,7 +462,7 @@ class User < ActiveRecord::Base
     aq = self.aspects.create(:name => I18n.t('aspects.seed.acquaintances'))
 
     if AppConfig.settings.autofollow_on_join?
-      default_account = Webfinger.new(AppConfig.settings.autofollow_on_join_user).fetch
+      default_account = Person.find_or_fetch_by_identifier(AppConfig.settings.autofollow_on_join_user)
       self.share_with(default_account, aq) if default_account
     end
     aq
diff --git a/app/workers/fetch_webfinger.rb b/app/workers/fetch_webfinger.rb
index 5dda819efbb2f9554e25fb4a2771183ab6e3993a..3b277824de14ce66f4d3692828413cb76e15da55 100644
--- a/app/workers/fetch_webfinger.rb
+++ b/app/workers/fetch_webfinger.rb
@@ -7,7 +7,7 @@ module Workers
     sidekiq_options queue: :socket_webfinger
 
     def perform(account)
-      person = Webfinger.new(account).fetch
+      person = Person.find_or_fetch_by_identifier(account)
 
       # also, schedule to fetch a few public posts from that person
       Diaspora::Fetcher::Public.queue_for(person) unless person.nil?
diff --git a/config/initializers/diaspora_federation.rb b/config/initializers/diaspora_federation.rb
index cedbf2e000c86477788bd212a181006180b0fbbd..f15dcf5cf74c96407b8c84ff6558486c0479a2b9 100644
--- a/config/initializers/diaspora_federation.rb
+++ b/config/initializers/diaspora_federation.rb
@@ -3,11 +3,13 @@ DiasporaFederation.configure do |config|
   # the pod url
   config.server_uri = AppConfig.pod_uri
 
+  config.certificate_authorities = AppConfig.environment.certificate_authorities.get
+
   config.define_callbacks do
-    on :person_webfinger_fetch do |handle|
+    on :fetch_person_for_webfinger do |handle|
       person = Person.find_local_by_diaspora_handle(handle)
       if person
-        DiasporaFederation::WebFinger::WebFinger.new(
+        DiasporaFederation::Discovery::WebFinger.new(
           acct_uri:    "acct:#{person.diaspora_handle}",
           alias_url:   AppConfig.url_to("/people/#{person.guid}"),
           hcard_url:   AppConfig.url_to(DiasporaFederation::Engine.routes.url_helpers.hcard_path(person.guid)),
@@ -21,10 +23,10 @@ DiasporaFederation.configure do |config|
       end
     end
 
-    on :person_hcard_fetch do |guid|
+    on :fetch_person_for_hcard do |guid|
       person = Person.find_local_by_guid(guid)
       if person
-        DiasporaFederation::WebFinger::HCard.new(
+        DiasporaFederation::Discovery::HCard.new(
           guid:             person.guid,
           nickname:         person.username,
           full_name:        "#{person.profile.first_name} #{person.profile.last_name}".strip,
@@ -39,5 +41,25 @@ DiasporaFederation.configure do |config|
         )
       end
     end
+
+    on :save_person_after_webfinger do |person|
+      # find existing person or create a new one
+      person_entity = Person.find_by(diaspora_handle: person.diaspora_id) ||
+        Person.new(diaspora_handle: person.diaspora_id, guid: person.guid,
+                   serialized_public_key: person.exported_key, url: person.url)
+
+      profile = person.profile
+      profile_entity = person_entity.profile ||= Profile.new
+
+      # fill or update profile
+      profile_entity.first_name = profile.first_name
+      profile_entity.last_name = profile.last_name
+      profile_entity.image_url = profile.image_url
+      profile_entity.image_url_medium = profile.image_url_medium
+      profile_entity.image_url_small = profile.image_url_small
+      profile_entity.searchable = profile.searchable
+
+      person_entity.save!
+    end
   end
 end
diff --git a/config/initializers/load_libraries.rb b/config/initializers/load_libraries.rb
index f0dfd8c0c8b54cf0c5d65cf996f1e7898cd3c8cd..86d701d33d8827b6be113648b6469a92548e0113 100644
--- a/config/initializers/load_libraries.rb
+++ b/config/initializers/load_libraries.rb
@@ -14,13 +14,10 @@ require 'diaspora'
 require 'direction_detector'
 require 'email_inviter'
 require 'evil_query'
-require 'h_card'
 require 'hydra_wrapper'
 require 'postzord'
 require 'publisher'
 require 'pubsubhubbub'
 require 'salmon'
 require 'stream'
-require 'webfinger'
-require 'webfinger_profile'
 require 'account_deleter'
diff --git a/lib/diaspora/fetcher/single.rb b/lib/diaspora/fetcher/single.rb
index 4e8340132c45c14c323f712e3afa32780929fdd6..797fc3cccafd32549be8d8baa0e282d174fa7de5 100644
--- a/lib/diaspora/fetcher/single.rb
+++ b/lib/diaspora/fetcher/single.rb
@@ -14,7 +14,7 @@ module Diaspora
         post = Post.where(guid: guid).first
         return post if post
 
-        post_author = Webfinger.new(author_id).fetch
+        post_author = Person.find_or_fetch_by_identifier(author_id)
         post_author.save! unless post_author.persisted?
 
         if fetched_post = fetch_post(post_author, guid)
diff --git a/lib/federated/relayable.rb b/lib/federated/relayable.rb
index a6cf6bf83cbd17c65e4826f4d94f08063ca1d4aa..7a1b13d6ccbd023f2fdd492f72ee9a308117dab0 100644
--- a/lib/federated/relayable.rb
+++ b/lib/federated/relayable.rb
@@ -24,7 +24,7 @@ module Federated
     end
 
     def diaspora_handle=(nh)
-      self.author = Webfinger.new(nh).fetch
+      self.author = Person.find_or_fetch_by_identifier(nh)
     end
 
     def parent_class
diff --git a/lib/h_card.rb b/lib/h_card.rb
deleted file mode 100644
index 2fda6aa2ed60da8ca4408dee0896abb1809bf0e6..0000000000000000000000000000000000000000
--- a/lib/h_card.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-#   Copyright (c) 2010-2011, Diaspora Inc.  This file is
-#   licensed under the Affero General Public License version 3 or later.  See
-#   the COPYRIGHT file.
-
-module HCard
-  def self.parse(doc)
-    {
-      given_name:   doc.css(".given_name").text,
-      family_name:  doc.css(".family_name").text,
-      url:          doc.css("#pod_location").text,
-      photo:        doc.css(".entity_photo .photo[src]").attribute("src").text,
-      photo_small:  doc.css(".entity_photo_small .photo[src]").attribute("src").text,
-      photo_medium: doc.css(".entity_photo_medium .photo[src]").attribute("src").text,
-      searchable:   doc.css(".searchable").text == "true"
-    }
-  end
-
-  def self.build(raw_hcard)
-    parse Nokogiri::HTML(raw_hcard)
-  end
-end
diff --git a/lib/postzord/receiver/private.rb b/lib/postzord/receiver/private.rb
index b29898df51ae024fdfbf60af16789e8ed2491549..5e545b9955a5475308eb665426cbee8ca401867a 100644
--- a/lib/postzord/receiver/private.rb
+++ b/lib/postzord/receiver/private.rb
@@ -9,7 +9,7 @@ class Postzord::Receiver::Private < Postzord::Receiver
     @user_person = @user.person
     @salmon_xml = opts[:salmon_xml]
 
-    @author = opts[:person] || Webfinger.new(salmon.author_id).fetch
+    @author = opts[:person] || Person.find_or_fetch_by_identifier(salmon.author_id)
 
     @object = opts[:object]
   end
@@ -56,7 +56,7 @@ class Postzord::Receiver::Private < Postzord::Receiver
     if @object.respond_to?(:relayable?)
       #if A and B are friends, and A sends B a comment from C, we delegate the validation to the owner of the post being commented on
       xml_author = @user.owns?(@object.parent) ? @object.diaspora_handle : @object.parent_author.diaspora_handle
-      @author = Webfinger.new(@object.diaspora_handle).fetch if @object.author
+      @author = Person.find_or_fetch_by_identifier(@object.diaspora_handle) if @object.author
     else
       xml_author = @object.diaspora_handle
     end
diff --git a/lib/postzord/receiver/public.rb b/lib/postzord/receiver/public.rb
index c96f0d340685d3aa69a5a4d1705f7ddc392f714b..fc870ba42325364e9005ad37a6173ecd5b2259fd 100644
--- a/lib/postzord/receiver/public.rb
+++ b/lib/postzord/receiver/public.rb
@@ -8,7 +8,7 @@ class Postzord::Receiver::Public < Postzord::Receiver
 
   def initialize(xml)
     @salmon = Salmon::Slap.from_xml(xml)
-    @author = Webfinger.new(@salmon.author_id).fetch
+    @author = Person.find_or_fetch_by_identifier(@salmon.author_id)
   end
 
   # @return [Boolean]
diff --git a/lib/webfinger.rb b/lib/webfinger.rb
deleted file mode 100644
index 5c28a4e90d19ab0f248d094bfdd2426e6ae6d360..0000000000000000000000000000000000000000
--- a/lib/webfinger.rb
+++ /dev/null
@@ -1,123 +0,0 @@
-#   Copyright (c) 2010-2012, Diaspora Inc.  This file is
-#   licensed under the Affero General Public License version 3 or later.  See
-#   the COPYRIGHT file.
-
-class Webfinger
-  include Diaspora::Logging
-
-  attr_accessor :host_meta_xrd, :webfinger_profile_xrd,
-                :webfinger_profile, :hcard, :hcard_xrd, :person,
-                :account, :ssl
-
-  def initialize(account)
-    self.account = account
-    self.ssl = true
-  end
-
-
-  def fetch
-    return person if existing_person_with_profile?
-    create_or_update_person_from_webfinger_profile!
-  end
-
-  def self.in_background(account, opts={})
-    Workers::FetchWebfinger.perform_async(account)
-  end
-
-  #everything below should be private I guess
-  def account=(str)
-    @account = str.strip.gsub('acct:','').to_s.downcase
-  end
-
-  def get(url)
-    logger.info "Getting: #{url} for #{account}"
-    begin
-      res = Faraday.get(url)
-      unless res.success?
-        raise "Failed to fetch #{url}: #{res.status}"
-      end
-      res.body
-    rescue OpenSSL::SSL::SSLError => e
-      logger.error "Failed to fetch #{url}: SSL setup invalid"
-      raise e
-    rescue => e
-      logger.error "Failed to fetch: #{url} for #{account}; #{e.message}"
-      raise e
-    end
-  end
-
-  def existing_person_with_profile?
-    cached_person.present? && cached_person.profile.present?
-  end
-
-  def cached_person
-    self.person ||= Person.by_account_identifier(account)
-  end
-
-  def create_or_update_person_from_webfinger_profile!
-    logger.info "webfingering #{account}, it is not known or needs updating"
-    if person #update my profile please
-      person.assign_new_profile_from_hcard(self.hcard)
-    else
-      person = make_person_from_webfinger
-    end
-    logger.info "successfully webfingered #{@account}" if person
-    person
-  end
-
-  #this tries the xrl url with https first, then falls back to http
-  def host_meta_xrd
-    begin
-      get(host_meta_url)
-    rescue => e
-      if self.ssl
-        self.ssl = false
-        retry
-      else
-        raise "there was an error getting the xrd from account #{@account}: #{e.message}"
-      end
-    end
-  end
-
-
-  def hcard
-    @hcard ||= HCard.build(hcard_xrd)
-  end
-
-  def webfinger_profile
-    @webfinger_profile ||= WebfingerProfile.new(account, webfinger_profile_xrd)
-  end
-
-  def hcard_url
-    self.webfinger_profile.hcard
-  end
-
-  def webfinger_profile_url
-    doc = Nokogiri::XML(self.host_meta_xrd)
-    return nil if doc.namespaces["xmlns"] != "http://docs.oasis-open.org/ns/xri/xrd-1.0"
-    swizzle doc.search('Link').find{|x| x['rel']=='lrdd'}['template']
-  end
-
-  def webfinger_profile_xrd
-    @webfinger_profile_xrd ||= get(webfinger_profile_url)
-    logger.warn "#{@account} doesn't exists anymore" if @webfinger_profile_xrd == false
-    @webfinger_profile_xrd
-  end
-
-  def hcard_xrd
-    @hcard_xrd ||= get(hcard_url)
-  end
-
-  def make_person_from_webfinger
-    Person.create_from_webfinger(webfinger_profile, hcard) unless webfinger_profile_xrd == false
-  end
-
-  def host_meta_url
-    domain = account.split('@')[1]
-    "http#{'s' if self.ssl}://#{domain}/.well-known/host-meta"
-  end
-
-  def swizzle(template)
-    template.gsub('{uri}', account)
-  end
-end
diff --git a/lib/webfinger_profile.rb b/lib/webfinger_profile.rb
deleted file mode 100644
index 44259e200196c3b3a72f24aac8817ecc2d4ceb97..0000000000000000000000000000000000000000
--- a/lib/webfinger_profile.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-class WebfingerProfile
-  include Diaspora::Logging
-
-  attr_accessor :webfinger_profile, :account, :links, :hcard, :guid, :public_key, :seed_location
-
-  def initialize(account, webfinger_profile)
-    @account = account
-    @webfinger_profile = webfinger_profile
-    @links = {}
-    set_fields
-  end
-
-  def valid_diaspora_profile?
-    !(@webfinger_profile.nil? || @account.nil? || @links.nil? || @hcard.nil? ||
-        @guid.nil? || @public_key.nil? || @seed_location.nil? )
-  end
-
-  private
-
-  def set_fields
-    doc = Nokogiri::XML.parse(webfinger_profile)
-    doc.remove_namespaces!
-
-    account_string = doc.css('Subject').text.gsub('acct:', '').strip
-
-    raise "account in profile(#{account_string}) and account requested (#{@account}) do not match" if account_string != @account
-
-    doc.css('Link').each do |l|
-      rel = text_of_attribute(l, 'rel')
-      href = text_of_attribute(l, 'href')
-      @links[rel] = href
-      case rel
-        when "http://microformats.org/profile/hcard"
-          @hcard = href
-        when "http://joindiaspora.com/guid"
-          @guid = href
-        when "http://joindiaspora.com/seed_location"
-          @seed_location = href
-      end
-    end
-
-    begin
-      pubkey = text_of_attribute( doc.at('Link[rel=diaspora-public-key]'), 'href')
-      @public_key = Base64.decode64 pubkey
-    rescue => e
-      logger.warn "event=invalid_profile identifier=#{@account}"
-    end
-  end
-
-  def text_of_attribute(doc, attr)
-    doc.attribute(attr) ? doc.attribute(attr).text : nil
-  end
-end
diff --git a/spec/controllers/diaspora_federation_controller_spec.rb b/spec/controllers/diaspora_federation_controller_spec.rb
deleted file mode 100644
index 8799220ee88660fe188f0c03bbe956382d3ea815..0000000000000000000000000000000000000000
--- a/spec/controllers/diaspora_federation_controller_spec.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-#   Copyright (c) 2010-2011, Diaspora Inc.  This file is
-#   licensed under the Affero General Public License version 3 or later.  See
-#   the COPYRIGHT file.
-
-require "spec_helper"
-
-# this is temporarily needed for fixture generation
-# TODO: remove this after the parsing is also in the diaspora_federation gem
-describe DiasporaFederation do
-  routes { DiasporaFederation::Engine.routes }
-
-  let(:fixture_path) { Rails.root.join("spec", "fixtures") }
-
-  describe DiasporaFederation::WebfingerController, type: :controller do
-    it "generates the host_meta fixture", fixture: true do
-      get :host_meta
-      expect(response).to be_success
-      expect(response.body).to match(/webfinger/)
-      save_fixture(response.body, "host-meta", fixture_path)
-    end
-
-    it "generates the webfinger fixture", fixture: true do
-      post :legacy_webfinger, "q" => alice.person.diaspora_handle
-      expect(response).to be_success
-      save_fixture(response.body, "webfinger", fixture_path)
-    end
-  end
-
-  describe DiasporaFederation::HCardController, type: :controller do
-    it "generates the hCard fixture", fixture: true do
-      post :hcard, "guid" => alice.person.guid.to_s
-      expect(response).to be_success
-      save_fixture(response.body, "hcard", fixture_path)
-    end
-  end
-end
diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb
index 32ee54b4f612ddaf44a88914a12f8f3f7229afaf..6866b712e5d88189d55f1fed6dc452181a42e35f 100644
--- a/spec/controllers/registrations_controller_spec.rb
+++ b/spec/controllers/registrations_controller_spec.rb
@@ -16,7 +16,7 @@ describe RegistrationsController, :type => :controller do
       :password_confirmation => "password"
       }
     }
-    allow(Webfinger).to receive_message_chain(:new, :fetch).and_return(FactoryGirl.create(:person))
+    allow(Person).to receive(:find_or_fetch_by_identifier).and_return(FactoryGirl.create(:person))
   end
 
   describe '#check_registrations_open!' do
diff --git a/spec/factories.rb b/spec/factories.rb
index a9989e077cfbde23e43c165b044d0d3ea1127147..02bfa2fc091ff09333f812a9389895a50553842d 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -301,4 +301,33 @@ FactoryGirl.define do
   end
 
   factory(:status, :parent => :status_message)
+
+  # Factories for the DiasporaFederation-gem
+
+  factory(:federation_person_from_webfinger, class: DiasporaFederation::Entities::Person) do
+    sequence(:guid) { UUID.generate :compact }
+    sequence(:diaspora_id) {|n| "bob-person-#{n}#{r_str}@example.net" }
+    url AppConfig.pod_uri.to_s
+    exported_key OpenSSL::PKey::RSA.generate(1024).public_key.export
+    profile {
+      DiasporaFederation::Entities::Profile.new(
+        FactoryGirl.attributes_for(:federation_profile_from_hcard, diaspora_id: diaspora_id))
+    }
+  end
+
+  factory(:federation_profile_from_hcard, class: DiasporaFederation::Entities::Profile) do
+    sequence(:diaspora_id) {|n| "bob-person-#{n}#{r_str}@example.net" }
+    sequence(:first_name) {|n| "My Name#{n}#{r_str}" }
+    last_name nil
+    image_url "/assets/user/default.png"
+    image_url_medium "/assets/user/default.png"
+    image_url_small "/assets/user/default.png"
+    searchable true
+  end
+
+  factory :federation_profile_from_hcard_with_image_url, parent: :federation_profile_from_hcard do
+    image_url "http://example.com/image.jpg"
+    image_url_medium "http://example.com/image_mid.jpg"
+    image_url_small "http://example.com/image_small.jpg"
+  end
 end
diff --git a/spec/federation_callbacks_spec.rb b/spec/federation_callbacks_spec.rb
index b0502198fb79eda84dbed5c5a7e01a74350b5f72..a2f3432b582cc50689d31d8bf6fe06bd8dcf3fec 100644
--- a/spec/federation_callbacks_spec.rb
+++ b/spec/federation_callbacks_spec.rb
@@ -1,10 +1,10 @@
 require "spec_helper"
 
 describe "diaspora federation callbacks" do
-  describe ":person_webfinger_fetch" do
+  describe ":fetch_person_for_webfinger" do
     it "returns a WebFinger instance with the data from the person" do
       person = alice.person
-      wf = DiasporaFederation.callbacks.trigger(:person_webfinger_fetch, alice.diaspora_handle)
+      wf = DiasporaFederation.callbacks.trigger(:fetch_person_for_webfinger, alice.diaspora_handle)
       expect(wf.acct_uri).to eq("acct:#{person.diaspora_handle}")
       expect(wf.alias_url).to eq(AppConfig.url_to("/people/#{person.guid}"))
       expect(wf.hcard_url).to eq(AppConfig.url_to("/hcard/users/#{person.guid}"))
@@ -17,15 +17,15 @@ describe "diaspora federation callbacks" do
     end
 
     it "returns nil if the person was not found" do
-      wf = DiasporaFederation.callbacks.trigger(:person_webfinger_fetch, "unknown@example.com")
+      wf = DiasporaFederation.callbacks.trigger(:fetch_person_for_webfinger, "unknown@example.com")
       expect(wf).to be_nil
     end
   end
 
-  describe ":person_hcard_fetch" do
+  describe ":fetch_person_for_hcard" do
     it "returns a HCard instance with the data from the person" do
       person = alice.person
-      hcard = DiasporaFederation.callbacks.trigger(:person_hcard_fetch, alice.guid)
+      hcard = DiasporaFederation.callbacks.trigger(:fetch_person_for_hcard, alice.guid)
       expect(hcard.guid).to eq(person.guid)
       expect(hcard.nickname).to eq(person.username)
       expect(hcard.full_name).to eq("#{person.profile.first_name} #{person.profile.last_name}")
@@ -44,13 +44,107 @@ describe "diaspora federation callbacks" do
       user.person.profile.last_name = nil
       user.person.profile.save
 
-      hcard = DiasporaFederation.callbacks.trigger(:person_hcard_fetch, user.guid)
+      hcard = DiasporaFederation.callbacks.trigger(:fetch_person_for_hcard, user.guid)
       expect(hcard.full_name).to eq(user.person.profile.first_name)
     end
 
     it "returns nil if the person was not found" do
-      hcard = DiasporaFederation.callbacks.trigger(:person_hcard_fetch, "1234567890abcdef")
+      hcard = DiasporaFederation.callbacks.trigger(:fetch_person_for_hcard, "1234567890abcdef")
       expect(hcard).to be_nil
     end
   end
+
+  describe ":save_person_after_webfinger" do
+    context "new person" do
+      it "creates a new person" do
+        person = DiasporaFederation::Entities::Person.new(FactoryGirl.attributes_for(:federation_person_from_webfinger))
+
+        DiasporaFederation.callbacks.trigger(:save_person_after_webfinger, person)
+
+        person_entity = Person.find_by(diaspora_handle: person.diaspora_id)
+        expect(person_entity.guid).to eq(person.guid)
+        expect(person_entity.serialized_public_key).to eq(person.exported_key)
+        expect(person_entity.url).to eq(person.url)
+
+        profile = person.profile
+        profile_entity = person_entity.profile
+        expect(profile_entity.first_name).to eq(profile.first_name)
+        expect(profile_entity.last_name).to eq(profile.last_name)
+        expect(profile_entity[:image_url]).to be_nil
+        expect(profile_entity[:image_url_medium]).to be_nil
+        expect(profile_entity[:image_url_small]).to be_nil
+        expect(profile_entity.searchable).to eq(profile.searchable)
+      end
+
+      it "creates a new person with images" do
+        person = DiasporaFederation::Entities::Person.new(
+          FactoryGirl.attributes_for(
+            :federation_person_from_webfinger,
+            profile: DiasporaFederation::Entities::Profile.new(
+              FactoryGirl.attributes_for(:federation_profile_from_hcard_with_image_url))
+          )
+        )
+
+        DiasporaFederation.callbacks.trigger(:save_person_after_webfinger, person)
+
+        person_entity = Person.find_by(diaspora_handle: person.diaspora_id)
+        expect(person_entity.guid).to eq(person.guid)
+        expect(person_entity.serialized_public_key).to eq(person.exported_key)
+        expect(person_entity.url).to eq(person.url)
+
+        profile = person.profile
+        profile_entity = person_entity.profile
+        expect(profile_entity.first_name).to eq(profile.first_name)
+        expect(profile_entity.last_name).to eq(profile.last_name)
+        expect(profile_entity.image_url).to eq(profile.image_url)
+        expect(profile_entity.image_url_medium).to eq(profile.image_url_medium)
+        expect(profile_entity.image_url_small).to eq(profile.image_url_small)
+        expect(profile_entity.searchable).to eq(profile.searchable)
+      end
+    end
+
+    context "update profile" do
+      let(:existing_person_entity) { FactoryGirl.create(:person) }
+      let(:person) {
+        DiasporaFederation::Entities::Person.new(
+          FactoryGirl.attributes_for(:federation_person_from_webfinger,
+                                     diaspora_id: existing_person_entity.diaspora_handle)
+        )
+      }
+
+      it "updates an existing profile" do
+        DiasporaFederation.callbacks.trigger(:save_person_after_webfinger, person)
+
+        person_entity = Person.find_by(diaspora_handle: existing_person_entity.diaspora_handle)
+
+        profile = person.profile
+        profile_entity = person_entity.profile
+        expect(profile_entity.first_name).to eq(profile.first_name)
+        expect(profile_entity.last_name).to eq(profile.last_name)
+      end
+
+      it "should not change the existing person" do
+        DiasporaFederation.callbacks.trigger(:save_person_after_webfinger, person)
+
+        person_entity = Person.find_by(diaspora_handle: existing_person_entity.diaspora_handle)
+        expect(person_entity.guid).to eq(existing_person_entity.guid)
+        expect(person_entity.serialized_public_key).to eq(existing_person_entity.serialized_public_key)
+        expect(person_entity.url).to eq(existing_person_entity.url)
+      end
+
+      it "creates profile for existing person if no profile present" do
+        existing_person_entity.profile = nil
+        existing_person_entity.save
+
+        DiasporaFederation.callbacks.trigger(:save_person_after_webfinger, person)
+
+        person_entity = Person.find_by(diaspora_handle: existing_person_entity.diaspora_handle)
+
+        profile = person.profile
+        profile_entity = person_entity.profile
+        expect(profile_entity.first_name).to eq(profile.first_name)
+        expect(profile_entity.last_name).to eq(profile.last_name)
+      end
+    end
+  end
 end
diff --git a/spec/fixtures/evan_hcard b/spec/fixtures/evan_hcard
deleted file mode 100644
index 8944ef1e3d114158d2a0bd319a11bda22672837b..0000000000000000000000000000000000000000
--- a/spec/fixtures/evan_hcard
+++ /dev/null
@@ -1,72 +0,0 @@
-   <body id="hcard">
-
-  <div id="wrap">
-   <div id="core">
-    <dl id="site_nav_local_views">
-     <dt>Local views</dt>
-     <dd></dd>
-</dl>
-    <div id="content">
-     <h1>Evan Prodromou</h1>
-
-     <div id="content_inner">
-      <div id="i" class="entity_profile vcard author">
-       <h2>User profile</h2>
-       <dl class="entity_depiction">
-        <dt>Photo</dt>
-        <dd>
-         <img src="http://avatar.status.net/evan/1-96-20100726204409.jpeg" class="photo avatar" width="96" height="96" alt="evan"/>
-</dd>
-
-</dl>
-       <dl class="entity_nickname">
-        <dt>Nickname</dt>
-        <dd>
-         <a href="http://evan.status.net/" rel="me" class="nickname url uid">evan</a>
-</dd>
-</dl>
-       <dl class="entity_fn">
-        <dt>Full name</dt>
-
-        <dd>
-         <span class="fn">Evan Prodromou</span>
-</dd>
-</dl>
-       <dl class="entity_location">
-        <dt>Location</dt>
-        <dd class="label">Montreal, QC, Canada</dd>
-</dl>
-       <dl class="entity_url">
-
-        <dt>URL</dt>
-        <dd>
-         <a href="http://evan.prodromou.name/" rel="me" class="url">http://evan.prodromou.name/</a>
-</dd>
-</dl>
-       <dl class="entity_note">
-        <dt>Note</dt>
-        <dd class="note">Montreal hacker and entrepreneur. Founder of identi.ca, lead developer of StatusNet, CEO of StatusNet Inc.</dd>
-
-</dl>
-</div>
-</div>
-</div>
-</div>
-   <div id="footer">
-    <dl id="licenses">
-     <dt id="site_statusnet_license">StatusNet software license</dt>
-     <dd><p><strong>Evan Prodromou</strong> is a microblogging service brought to you by <a href="http://status.net/">Status.net</a>. It runs the <a href="http://status.net/">StatusNet</a> microblogging software, version 0.9.5, available under the <a href="http://www.fsf.org/licensing/licenses/agpl-3.0.html">GNU Affero General Public License</a>.</p>
-
-</dd>
-     <dt id="site_content_license">Site content license</dt>
-     <dd id="site_content_license_cc">
-      <p>
-       <img id="license_cc" src="http://i.creativecommons.org/l/by/3.0/80x15.png" alt="Creative Commons Attribution 3.0" width="80" height="15"/>:w
-
- All Evan Prodromou content and data are available under the <a class="license" rel="external license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0</a> license.</p>
-</dd>
-
-</dl>
-</div>
-</div>
-</body>
diff --git a/spec/fixtures/finger_xrd b/spec/fixtures/finger_xrd
deleted file mode 100644
index 5ede32d744fdeceaa2e56468534e3c37710547f0..0000000000000000000000000000000000000000
--- a/spec/fixtures/finger_xrd
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
-  <Subject>acct:tom@tom.joindiaspora.com</Subject>
-  <Alias>"http://tom.joindiaspora.com/"</Alias>
-  <Link rel="http://microformats.org/profile/hcard" type="text/html" href="http://tom.joindiaspora.com/hcard/users/4c8eccce34b7da59ff000002"/>
-  <Link rel="http://joindiaspora.com/seed_location" type="text/html" href="http://tom.joindiaspora.com/"/>
-  <Link rel="http://joindiaspora.com/guid" type="text/html" href="4c8eccce34b7da59ff000002"/>
-  <Link rel='http://webfinger.net/rel/profile-page' type='text/html' href="http://tom.joindiaspora.com/u/tom"/>
-  <Link rel="http://schemas.google.com/g/2010#updates-from" type="application/atom+xml" href="http://tom.joindiaspora.com/u/tom.atom"/>
-  <Link rel="diaspora-public-key" type="RSA" href="LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUNDZ0tDQWdFQXlt dHpUdWQ3SytXQklPVVYwMmxZN2Z1NjdnNWQrbTBra1ZIQlgzTk1uYXB5bnZL a0VSemoKbkxma2JrTVpEVGdPNG1UaThmWFI3Q1ZSK3Q1SFN4b2Vub0JWazVX eUFabkEzWmpTRjBPcC9RakhlYzhvK0dVSApDOFluNFJ5N01hQ0R1cUNpNnJv c2RlbUlLTm1Fa2dsVVY1VzZ4WFd4Vmtrb21oL2VCQ2FmaVdMTXFRMG82NGox Ckw3aXNjQjVOM3ZkbnBrUmU3SkFxLzNDUTI3dWhDS0ZIWG1JYm1iVmhJQTNC R0J6YStPV3NjK1Z5cjV0Mm1wSlIKU1RXMk9UL20rS0NPK21kdnpmenQ0TzEr UHc1M1pJMjRpMlc2cW1XdThFZ1Z6QVcyMStuRGJManZiNHpzVHlrNQppN1JM cG8rUFl2VUJlLy8wM1lkQUJoRlJhVXpTL0RtcWRubEVvb0VvK0VmYzRkQ1NF bWVkMUgrek01c2xqQm1rCks5amsvOHNQZDB0SVZmMWZXdW9BcWZTSmErSXdr OHNybkdZbEVlaFV1dVhIY0x2b2JlUXJKYWdiRGc1Qll5TnIKeTAzcHpKTHlS ZU9UcC9RK1p0TXpMOFJMZWJsUzlWYXdNQzNDVzc5K0RGditTWGZ0eTl3NC8w d2NpUHpKejg2bgp2VzJ5K3crTThOWG52enBWNU81dGI4azZxZ2N0WjBmRzFu eXQ0RklsSHNVaUVoNnZLZmNLSmFPeWFRSGNGcWVxCjkwUkpoMm9TMDJBdFJx TFRSWDJJQjFnYXZnWEFXN1NYanJNbUNlVzlCdVBKYU5nZkp3WFFaelVoa0tC V1k0VnMKZTRFVWRob3R5RWkvUmE0RXVZU01ZcnZEeUFRUHJsY0wveDliaU1p bHVPcU9OMEpJZ1VodEZQRUNBd0VBQVE9PQotLS0tLUVORCBSU0EgUFVCTElD IEtFWS0tLS0tCg== "/>
-</XRD>
diff --git a/spec/fixtures/hcard_response b/spec/fixtures/hcard_response
deleted file mode 100644
index 302c02430441d47e07fae1f8e1d373003c98bec2..0000000000000000000000000000000000000000
--- a/spec/fixtures/hcard_response
+++ /dev/null
@@ -1,64 +0,0 @@
-
-<div id='content'> 
-  <h1>Alexander Hamiltom</h1> 
-  <div id='content_inner'> 
-    <div class='entity_profile vcard author' id='i'> 
-      <h2>User profile</h2> 
-      <dl class='entity_nickname'> 
-        <dt>Nickname</dt> 
-        <dd> 
-          <a class='nickname url uid' href='http://localhost:3000/' rel='me'>Alexander Hamiltom</a> 
-        </dd> 
-      </dl> 
-      <dl class='entity_given_name'> 
-        <dt>First name</dt> 
-        <dd> 
-          <span class='given_name'>Alexander</span> 
-        </dd> 
-      </dl> 
-      <dl class='entity_family_name'> 
-        <dt>Family name</dt> 
-        <dd> 
-          <span class='family_name'>Hamiltom</span> 
-        </dd> 
-      </dl> 
-      <dl class='entity_fn'> 
-        <dt>Full name</dt> 
-        <dd> 
-          <span class='fn'>Alexander Hamiltom</span> 
-        </dd> 
-      </dl> 
-      <dl class='entity_url'> 
-        <dt>URL</dt> 
-        <dd> 
-          <a class='url' href='http://localhost:3000/' id='pod_location' rel='me'>http://localhost:3000/</a> 
-        </dd> 
-      </dl> 
-      <dl class='entity_photo'> 
-        <dt>Photo</dt> 
-        <dd> 
-          <img class='photo avatar' height='300px' src='http://localhost:3000/uploads/images/thumb_large_8rxQAwC4Vx4cf5667d37db5b0fef000003.jpg' width='300px'> 
-        </dd> 
-      </dl> 
-      <dl class='entity_photo_medium'> 
-        <dt>Photo</dt> 
-        <dd> 
-          <img class='photo avatar' height='100px' src='http://localhost:3000/uploads/images/thumb_medium_8rxQAwC4Vx4cf5667d37db5b0fef000003.jpg' width='100px'> 
-        </dd> 
-      </dl> 
-      <dl class='entity_photo_small'> 
-        <dt>Photo</dt> 
-        <dd> 
-          <img class='photo avatar' height='50px' src='http://localhost:3000/uploads/images/thumb_small_8rxQAwC4Vx4cf5667d37db5b0fef000003.jpg' width='50px'> 
-        </dd> 
-      </dl> 
-      <dl class='entity_searchable'> 
-        <dt>Searchable</dt> 
-        <dd> 
-          <span class='searchable'>false</span> 
-        </dd> 
-      </dl> 
-    </div> 
-  </div> 
-</div> 
-
diff --git a/spec/fixtures/host_xrd b/spec/fixtures/host_xrd
deleted file mode 100644
index ca2d3b5fcbe9c16a047255138fd7473d5465697e..0000000000000000000000000000000000000000
--- a/spec/fixtures/host_xrd
+++ /dev/null
@@ -1,7 +0,0 @@
-  <?xml version='1.0' encoding='UTF-8'?>
-  <XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>
-    <Link rel='lrdd'
-          template='http://tom.joindiaspora.com/webfinger/?q={uri}'>
-      <Title>Resource Descriptor</Title>
-    </Link>
-  </XRD>
diff --git a/spec/fixtures/nonseed_finger_xrd b/spec/fixtures/nonseed_finger_xrd
deleted file mode 100644
index b26e7d15d6449c3fa4e123ec82d7eca2921925de..0000000000000000000000000000000000000000
--- a/spec/fixtures/nonseed_finger_xrd
+++ /dev/null
@@ -1,15 +0,0 @@
-      <XRD>
-      <Subject>acct:evan@status.net</Subject>
-      <Alias>acct:evan@evan.status.net</Alias>
-      <Alias>http://evan.status.net/user/1</Alias>
-      <Link rel="http://webfinger.net/rel/profile-page" type="text/html" href="http://evan.status.net/user/1"/>
-      <Link rel="http://schemas.google.com/g/2010#updates-from" href="http://evan.status.net/api/statuses/user_timeline/1.atom" type="application/atom+xml"/>
-      <Link rel="http://microformats.org/profile/hcard" type="text/html" href="http://evan.status.net/hcard"/>
-      <Link rel="http://gmpg.org/xfn/11" type="text/html" href="http://evan.status.net/user/1"/>
-      <Link rel="describedby" type="application/rdf+xml" href="http://evan.status.net/foaf"/>
-      <Link rel="salmon" href="http://evan.status.net/main/salmon/user/1"/>
-      <Link rel="http://salmon-protocol.org/ns/salmon-replies" href="http://evan.status.net/main/salmon/user/1"/>
-      <Link rel="http://salmon-protocol.org/ns/salmon-mention" href="http://evan.status.net/main/salmon/user/1"/>
-      <Link rel="magic-public-key" href="data:application/magic-public-key,RSA.vyohOlwX03oJUg6R8BQP4V-6QQUfPg9gzOwk3ENQjqeGorHN8RNI4rhCQp7tACe9DEdEKtzZHbSvQC2zRICQ9JG_SIcpiU9jcT2imN5cPLZZQuPFZWwG4xPu_8LKRHuXeLGkzQMjvg6jFBl7qdo_iPnlbtIBb-mEuAnfRMcdUPE=.AQAB"/>
-      <Link rel="http://ostatus.org/schema/1.0/subscribe" template="http://evan.status.net/main/ostatussub?profile={uri}"/>
-      </XRD>
diff --git a/spec/helper_methods.rb b/spec/helper_methods.rb
index e0df90faa383ff051a1e4ce1faa35c3a6ac14c04..ad4371a347a1de12a7211364d0807c8f05a7a30b 100644
--- a/spec/helper_methods.rb
+++ b/spec/helper_methods.rb
@@ -19,46 +19,6 @@ module HelperMethods
                            :receiving => true)
   end
 
-  def stub_success(address = 'abc@example.com', opts = {})
-    host = address.split('@')[1]
-    stub_request(:get, "https://#{host}/.well-known/host-meta").to_return(:status => 200, :body => host_xrd)
-    stub_request(:get, "http://#{host}/.well-known/host-meta").to_return(:status => 200, :body => host_xrd)
-    if opts[:diaspora] || host.include?("diaspora")
-      stub_request(:get, /webfinger\/\?q=#{address}/).to_return(:status => 200, :body => finger_xrd)
-      stub_request(:get, "http://#{host}/hcard/users/4c8eccce34b7da59ff000002").to_return(:status => 200, :body => hcard_response)
-    else
-      stub_request(:get, /webfinger\/\?q=#{address}/).to_return(:status => 200, :body => nonseed_finger_xrd)
-      stub_request(:get, 'http://evan.status.net/hcard').to_return(:status => 200, :body => evan_hcard)
-    end
-  end
-
-  def stub_failure(address = 'abc@example.com')
-    host = address.split('@')[1]
-    stub_request(:get, "https://#{host}/.well-known/host-meta").to_return(:status => 200, :body => host_xrd)
-    stub_request(:get, "http://#{host}/.well-known/host-meta").to_return(:status => 200, :body => host_xrd)
-    stub_request(:get, /webfinger\/\?q=#{address}/).to_return(:status => 500)
-  end
-
-  def host_xrd
-    File.open(File.dirname(__FILE__) + '/fixtures/host_xrd').read
-  end
-
-  def finger_xrd
-    File.open(File.dirname(__FILE__) + '/fixtures/finger_xrd').read
-  end
-
-  def hcard_response
-    File.open(File.dirname(__FILE__) + '/fixtures/hcard_response').read
-  end
-
-  def nonseed_finger_xrd
-    File.open(File.dirname(__FILE__) + '/fixtures/nonseed_finger_xrd').read
-  end
-
-  def evan_hcard
-    File.open(File.dirname(__FILE__) + '/fixtures/evan_hcard').read
-  end
-
   def uploaded_photo
     fixture_filename = 'button.png'
     fixture_name = File.join(File.dirname(__FILE__), 'fixtures', fixture_filename)
diff --git a/spec/integration/receiving_spec.rb b/spec/integration/receiving_spec.rb
index 1d31116e91cccbb5b204cfc307c79bf7313b4411..c62241cf7a15db4f7e48af69ede4245eee259feb 100644
--- a/spec/integration/receiving_spec.rb
+++ b/spec/integration/receiving_spec.rb
@@ -225,11 +225,10 @@ describe 'a user receives a post', :type => :request do
         Profile.where(:person_id => remote_person.id).delete_all
         remote_person.attributes.delete(:id) # leaving a nil id causes it to try to save with id set to NULL in postgres
 
-        m = double()
-        expect(Webfinger).to receive(:new).twice.with(eve.person.diaspora_handle).and_return(m)
         remote_person.save(:validate => false)
         remote_person.profile = FactoryGirl.create(:profile, :person => remote_person)
-        expect(m).to receive(:fetch).twice.and_return(remote_person)
+        expect(Person).to receive(:find_or_fetch_by_identifier).twice.with(eve.person.diaspora_handle)
+                            .and_return(remote_person)
 
         expect(bob.reload.visible_shareables(Post).size).to eq(1)
         post_in_db = StatusMessage.find(@post.id)
diff --git a/spec/lib/h_card_spec.rb b/spec/lib/h_card_spec.rb
deleted file mode 100644
index d4197c0b44699a1b81c3b8641a95765d1e3c6814..0000000000000000000000000000000000000000
--- a/spec/lib/h_card_spec.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-#   Copyright (c) 2010-2011, Diaspora Inc.  This file is
-#   licensed under the Affero General Public License version 3 or later.  See
-#   the COPYRIGHT file.
-
-require "spec_helper"
-
-describe HCard do
-  it "should parse an hcard" do
-    raw_hcard = hcard_response
-    hcard = HCard.build raw_hcard
-    expect(hcard[:family_name].include?("Hamiltom")).to be true
-    expect(hcard[:given_name].include?("Alex")).to be true
-    expect(hcard[:photo].include?("thumb_large")).to be true
-    expect(hcard[:photo_medium].include?("thumb_medium")).to be true
-    expect(hcard[:photo_small].include?("thumb_small")).to be true
-    expect(hcard[:url]).to eq("http://localhost:3000/")
-    expect(hcard[:searchable]).to eq(false)
-  end
-
-  it "should parse an hcard with searchable true" do
-    raw_hcard = hcard_response.sub("<span class='searchable'>false</span>", "<span class='searchable'>true</span>")
-    hcard = HCard.build raw_hcard
-    expect(hcard[:family_name].include?("Hamiltom")).to be true
-    expect(hcard[:given_name].include?("Alex")).to be true
-    expect(hcard[:photo].include?("thumb_large")).to be true
-    expect(hcard[:photo_medium].include?("thumb_medium")).to be true
-    expect(hcard[:photo_small].include?("thumb_small")).to be true
-    expect(hcard[:url]).to eq("http://localhost:3000/")
-    expect(hcard[:searchable]).to eq(true)
-  end
-
-  it "should parse an hcard with empty searchable" do
-    raw_hcard = hcard_response.sub("<span class='searchable'>false</span>", "<span class='searchable'></span>")
-    hcard = HCard.build raw_hcard
-    expect(hcard[:family_name].include?("Hamiltom")).to be true
-    expect(hcard[:given_name].include?("Alex")).to be true
-    expect(hcard[:photo].include?("thumb_large")).to be true
-    expect(hcard[:photo_medium].include?("thumb_medium")).to be true
-    expect(hcard[:photo_small].include?("thumb_small")).to be true
-    expect(hcard[:url]).to eq("http://localhost:3000/")
-    expect(hcard[:searchable]).to eq(false)
-  end
-end
diff --git a/spec/lib/postzord/receiver/private_spec.rb b/spec/lib/postzord/receiver/private_spec.rb
index 717dbe8c13c063bf84fd1b1fbd2a16708531db6d..088df5ecf835fdc255766c6dd4b4fa52a94670dc 100644
--- a/spec/lib/postzord/receiver/private_spec.rb
+++ b/spec/lib/postzord/receiver/private_spec.rb
@@ -13,7 +13,7 @@ describe Postzord::Receiver::Private do
 
   describe '.initialize' do
     it 'valid for local' do
-      expect(Webfinger).not_to receive(:new)
+      expect(Person).not_to receive(:find_or_fetch_by_identifier)
       expect(Salmon::EncryptedSlap).not_to receive(:from_xml)
 
       zord = Postzord::Receiver::Private.new(bob, :person => alice.person, :object => @alices_post)
@@ -24,11 +24,9 @@ describe Postzord::Receiver::Private do
 
     it 'valid for remote' do
       salmon_double = double()
-      web_double = double()
-      expect(web_double).to receive(:fetch).and_return true
       expect(salmon_double).to receive(:author_id).and_return(true)
       expect(Salmon::EncryptedSlap).to receive(:from_xml).with(@salmon_xml, bob).and_return(salmon_double)
-      expect(Webfinger).to receive(:new).and_return(web_double)
+      expect(Person).to receive(:find_or_fetch_by_identifier).and_return(true)
 
       zord = Postzord::Receiver::Private.new(bob, :salmon_xml => @salmon_xml)
       expect(zord.instance_variable_get(:@user)).not_to be_nil
diff --git a/spec/lib/webfinger_profile_spec.rb b/spec/lib/webfinger_profile_spec.rb
deleted file mode 100644
index 51991f084589802449ab3b17961a67455bba33fb..0000000000000000000000000000000000000000
--- a/spec/lib/webfinger_profile_spec.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-require 'spec_helper'
-
-describe WebfingerProfile do
-  let(:webfinger_profile){File.open(Rails.root.join("spec", "fixtures", "finger_xrd")).read.strip}
-  let(:not_diaspora_webfinger){File.open(Rails.root.join("spec", "fixtures", "nonseed_finger_xrd")).read.strip}
-
-  let(:account){"tom@tom.joindiaspora.com"}
-  let(:profile){ WebfingerProfile.new(account, webfinger_profile) }
-  
-  context "parsing a diaspora profile" do
-    
-    describe '#valid_diaspora_profile?' do
-      it 'should check all of the required fields' do
-        expect(manual_nil_check(profile)).to eq(profile.valid_diaspora_profile?)
-      end
-    end
-
-    describe '#set_fields' do
-      it 'should check to make sure it has a the right webfinger profile' do
-        expect{ WebfingerProfile.new("nottom@tom.joindiaspora.com", webfinger_profile)}.to raise_error 
-      end
-
-      it 'should handle a non-diaspora profile without blowing up' do
-        expect{ WebfingerProfile.new("evan@status.net", not_diaspora_webfinger)}.not_to raise_error 
-      end
-      
-      [:links, :hcard, :guid, :seed_location, :public_key].each do |field|
-        it 'should sets the #{field} field' do
-          expect(profile.send(field)).to be_present
-        end
-      end
-    end
-  end
-
-    def manual_nil_check(profile)
-      profile.instance_variables.each do |var|
-        var = var.to_s.gsub('@', '')
-        return false if profile.send(var).nil? == true
-      end
-      true
-    end
-end
diff --git a/spec/lib/webfinger_spec.rb b/spec/lib/webfinger_spec.rb
deleted file mode 100644
index c3515bebe26ee3c294f0e8c3ad9cab3e3d8e6b93..0000000000000000000000000000000000000000
--- a/spec/lib/webfinger_spec.rb
+++ /dev/null
@@ -1,221 +0,0 @@
-#   Copyright (c) 2010-2011, Diaspora Inc.  This file is
-#   licensed under the Affero General Public License version 3 or later.  See
-#   the COPYRIGHT file.
-
-require 'spec_helper'
-
-describe Webfinger do
-  let(:host_meta_xrd) { File.open(Rails.root.join('spec', 'fixtures', 'host-meta.fixture.html')).read }
-  let(:webfinger_xrd) { File.open(Rails.root.join('spec', 'fixtures', 'webfinger.fixture.html')).read }
-  let(:hcard_xml) { File.open(Rails.root.join('spec', 'fixtures', 'hcard.fixture.html')).read }
-  let(:account){'foo@bar.com'}
-  let(:account_in_fixtures){"alice@localhost:9887"}
-  let(:finger){Webfinger.new(account)}
-  let(:host_meta_url){"http://#{AppConfig.pod_uri.authority}/webfinger?q="}
-
-  describe '#intialize' do
-    it 'sets account ' do
-      n = Webfinger.new("mbs348@gmail.com")
-      expect(n.account).not_to be nil
-    end
-
-    it "downcases account and strips whitespace, and gsub 'acct:'" do
-      n = Webfinger.new("acct:BIGBOY@Example.Org ")
-      expect(n.account).to eq('bigboy@example.org')
-    end
-
-    it 'should set ssl as the default' do
-      foo = Webfinger.new(account)
-      expect(foo.ssl).to be true
-    end
-  end
-
-  describe '.in_background' do
-    it 'enqueues a Workers::FetchWebfinger job' do
-      expect(Workers::FetchWebfinger).to receive(:perform_async).with(account)
-      Webfinger.in_background(account)
-    end
-  end
-
-  describe '#fetch' do
-    it 'works' do
-      finger = Webfinger.new(account_in_fixtures)
-      allow(finger).to receive(:host_meta_xrd).and_return(host_meta_xrd)
-      allow(finger).to receive(:hcard_xrd).and_return(hcard_xml)
-      allow(finger).to receive(:webfinger_profile_xrd).and_return(webfinger_xrd)
-      person = finger.fetch
-      expect(person).to be_valid
-      expect(person).to be_a Person
-    end
-
-  end
-
-  describe '#get' do
-    it 'makes a request and grabs the body' do
-      url ="https://bar.com/.well-known/host-meta"
-      stub_request(:get, url).
-        to_return(:status => 200, :body => host_meta_xrd)
-
-      expect(finger.get(url)).to eq(host_meta_xrd)
-    end
-
-    it 'follows redirects' do
-      redirect_url = "http://whereami.whatisthis/host-meta"
-
-      stub_request(:get, "https://bar.com/.well-known/host-meta").
-        to_return(:status => 302, :headers => { 'Location' => redirect_url })
-
-      stub_request(:get, redirect_url).
-        to_return(:status => 200, :body => host_meta_xrd)
-
-      finger.host_meta_xrd
-
-      expect(a_request(:get, redirect_url)).to have_been_made
-    end
-
-    it 'raises on 404' do
-      url ="https://bar.com/.well-known/host-meta"
-      stub_request(:get, url).
-        to_return(:status => 404, :body => nil)
-
-      expect {
-        expect(finger.get(url)).to eq(false)
-      }.to raise_error
-    end
-  end
-
-  describe 'existing_person_with_profile?' do
-    it 'returns true if cached_person is present and has a profile' do
-      expect(finger).to receive(:cached_person).twice.and_return(FactoryGirl.create(:person))
-      expect(finger.existing_person_with_profile?).to be true
-    end
-
-    it 'returns false if it has no person' do
-      allow(finger).to receive(:cached_person).and_return false
-      expect(finger.existing_person_with_profile?).to be false
-    end
-
-    it 'returns false if the person has no profile' do
-      p = FactoryGirl.create(:person)
-      p.profile = nil
-      allow(finger).to receive(:cached_person).and_return(p)
-      expect(finger.existing_person_with_profile?).to be false
-    end
-  end
-
-  describe 'cached_person' do
-    it 'sets the person by looking up the account from Person.by_account_identifier' do
-      person = double
-      expect(Person).to receive(:by_account_identifier).with(account).and_return(person)
-      expect(finger.cached_person).to eq(person)
-      expect(finger.person).to eq(person)
-    end
-  end
-
-
-  describe 'create_or_update_person_from_webfinger_profile!' do
-    context 'with a cached_person' do
-      it 'calls Person#assign_new_profile_from_hcard with the fetched hcard' do
-        finger.hcard_xrd = hcard_xml
-        allow(finger).to receive(:person).and_return(bob.person)
-        expect(bob.person).to receive(:assign_new_profile_from_hcard).with(finger.hcard)
-        finger.create_or_update_person_from_webfinger_profile!
-      end
-    end
-
-    context 'with no cached person' do
-      it 'sets person based on make_person_from_webfinger' do
-        allow(finger).to receive(:person).and_return(nil)
-        expect(finger).to receive(:make_person_from_webfinger)
-        finger.create_or_update_person_from_webfinger_profile!
-      end
-    end
-  end
-
-  describe '#host_meta_xrd' do
-    it 'calls #get with host_meta_url' do
-      allow(finger).to receive(:host_meta_url).and_return('meta')
-      expect(finger).to receive(:get).with('meta')
-      finger.host_meta_xrd
-    end
-
-    it 'should retry with ssl off a second time' do
-      expect(finger).to receive(:get).and_raise(StandardError)
-      expect(finger).to receive(:get)
-      finger.host_meta_xrd
-      expect(finger.ssl).to be false
-    end
-  end
-
-  describe '#hcard' do
-    it 'calls HCard.build' do
-      allow(finger).to receive(:hcard_xrd).and_return(hcard_xml)
-      expect(HCard).to receive(:build).with(hcard_xml).and_return true
-      expect(finger.hcard).not_to be_nil
-    end
-  end
-
-  describe '#webfinger_profile' do
-    it 'constructs a new WebfingerProfile object' do
-      allow(finger).to receive(:webfinger_profile_xrd).and_return(webfinger_xrd)
-      expect(WebfingerProfile).to receive(:new).with(account, webfinger_xrd)
-      finger.webfinger_profile
-    end
-  end
-
-  describe '#webfinger_profile_url' do
-    it 'returns the llrd link for a valid host meta' do
-      allow(finger).to receive(:host_meta_xrd).and_return(host_meta_xrd)
-      expect(finger.webfinger_profile_url).not_to be_nil
-    end
-
-    it 'returns nil if no link is found' do
-      allow(finger).to receive(:host_meta_xrd).and_return(nil)
-      expect(finger.webfinger_profile_url).to be_nil
-    end
-  end
-
-  describe '#webfinger_profile_xrd' do
-    it 'calls #get with the hcard_url' do
-      allow(finger).to receive(:hcard_url).and_return("url")
-      expect(finger).to receive(:get).with("url")
-      finger.hcard_xrd
-    end
-  end
-
-  describe '#make_person_from_webfinger' do
-    it 'with an hcard and a webfinger_profile, it calls Person.create_from_webfinger' do
-      allow(finger).to receive(:hcard).and_return("hcard")
-      allow(finger).to receive(:webfinger_profile_xrd).and_return("webfinger_profile_xrd")
-      allow(finger).to receive(:webfinger_profile).and_return("webfinger_profile")
-      expect(Person).to receive(:create_from_webfinger).with("webfinger_profile", "hcard")
-      finger.make_person_from_webfinger
-    end
-
-    it 'with an false xrd it does not call Person.create_from_webfinger' do
-      allow(finger).to receive(:webfinger_profile_xrd).and_return(false)
-      expect(Person).not_to receive(:create_from_webfinger)
-      finger.make_person_from_webfinger
-    end
-  end
-
-
-
-  describe '#host_meta_url' do
-    it 'should return canonical host-meta url for http' do
-      finger.ssl = false
-      expect(finger.host_meta_url).to eq("http://bar.com/.well-known/host-meta")
-    end
-
-    it 'can return the https version' do
-      expect(finger.host_meta_url).to eq("https://bar.com/.well-known/host-meta")
-    end
-  end
-
-  describe 'swizzle' do
-    it 'gsubs out {uri} for the account' do
-      string = "{uri} is the coolest"
-      expect(finger.swizzle(string)).to eq("#{finger.account} is the coolest")
-    end
-  end
-end
diff --git a/spec/models/reshare_spec.rb b/spec/models/reshare_spec.rb
index ff1781b9a330de3f85865f72ff29acc0867af9a9..e235c40666cf3595af469c99728b9487184aabc0 100644
--- a/spec/models/reshare_spec.rb
+++ b/spec/models/reshare_spec.rb
@@ -212,9 +212,7 @@ describe Reshare, :type => :model do
 
           @original_author.profile = @original_profile
 
-          wf_prof_double = double
-          expect(wf_prof_double).to receive(:fetch).and_return(@original_author)
-          expect(Webfinger).to receive(:new).and_return(wf_prof_double)
+          expect(Person).to receive(:find_or_fetch_by_identifier).and_return(@original_author)
 
           allow(@response).to receive(:body).and_return(@root_object.to_diaspora_xml)
 
@@ -287,10 +285,7 @@ describe Reshare, :type => :model do
             @xml = @reshare.to_xml.to_s
 
             different_person = FactoryGirl.build(:person)
-
-            wf_prof_double = double
-            expect(wf_prof_double).to receive(:fetch).and_return(different_person)
-            expect(Webfinger).to receive(:new).and_return(wf_prof_double)
+            expect(Person).to receive(:find_or_fetch_by_identifier).and_return(different_person)
 
             allow(different_person).to receive(:url).and_return(@original_author.url)
 
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 03f50b9c02e41253dbb9f7a10bfe3f2bdeac32d6..c9cede84e6661801608c1e00c7c6a4bedfc26190 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -912,11 +912,9 @@ describe User, :type => :model do
       context "with autofollow sharing enabled" do
         it "should start sharing with autofollow account" do
           AppConfig.settings.autofollow_on_join = true
-          AppConfig.settings.autofollow_on_join_user = 'one'
+          AppConfig.settings.autofollow_on_join_user = "one"
 
-          wf_double = double
-          expect(wf_double).to receive(:fetch)
-          expect(Webfinger).to receive(:new).with('one').and_return(wf_double)
+          expect(Person).to receive(:find_or_fetch_by_identifier).with("one")
 
           user.seed_aspects
         end
@@ -926,7 +924,7 @@ describe User, :type => :model do
         it "should not start sharing with the diasporahq account" do
           AppConfig.settings.autofollow_on_join = false
 
-          expect(Webfinger).not_to receive(:new)
+          expect(Person).not_to receive(:find_or_fetch_by_identifier)
 
           user.seed_aspects
         end
diff --git a/spec/workers/fetch_webfinger_spec.rb b/spec/workers/fetch_webfinger_spec.rb
index cfa83b24a24b65cddb715eb0842653036443a9eb..0a9469c75d7304d01de45631e8e3da723e540758 100644
--- a/spec/workers/fetch_webfinger_spec.rb
+++ b/spec/workers/fetch_webfinger_spec.rb
@@ -3,7 +3,7 @@ require "spec_helper"
 describe Workers::FetchWebfinger do
   it "should webfinger and queue a job to fetch public posts" do
     @person = FactoryGirl.create(:person)
-    allow(Webfinger).to receive(:new).and_return(double(fetch: @person))
+    allow(Person).to receive(:find_or_fetch_by_identifier).and_return(@person)
 
     expect(Diaspora::Fetcher::Public).to receive(:queue_for).exactly(1).times
 
@@ -11,7 +11,7 @@ describe Workers::FetchWebfinger do
   end
 
   it "should webfinger and queue no job to fetch public posts if the person is not found" do
-    allow(Webfinger).to receive(:new).and_return(double(fetch: nil))
+    allow(Person).to receive(:find_or_fetch_by_identifier).and_return(nil)
 
     expect(Diaspora::Fetcher::Public).not_to receive(:queue_for)