<?xml version='1.0' encoding='utf-8' ?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Social Memory Complex: devise</title>
<link href="https://www.socialmemorycomplex.net/tags/devise/feed.xml" rel="self" />
<link href="https://www.socialmemorycomplex.net/tags/devise/" />
<updated>2026-05-24T21:17:06+00:00</updated>
<id>https://www.socialmemorycomplex.net/tags/devise/</id>
<entry>
  <title>Embedding users in accounts with Devise and Mongoid</title>
  <link href="http://socialmemorycomplex.net/2013/01/04/embedding-users-in-accounts-with-devise/" />
  <updated>2013-01-04T00:00:00+00:00</updated>
  <id>http://socialmemorycomplex.net/2013/01/04/embedding-users-in-accounts-with-devise/</id>
  <author><name>Jeremy Weiland</name></author>
  <content type="html"><![CDATA[<p><a href="https://gist.github.com/446144">This gist</a> referred to by the <a href="https://github.com/plataformatec/devise/wiki/How-To:-Embed-users-in-your-account-model-with-Mongoid">Devise wiki</a> is no longer accurate as far as I can tell.  So I wanted to share my approach for how to use devise in a situation where user documents are embedded in account documents, especially in the scenario where your account has a subdomain assigned to it.</p>

<p>Obviously, the first thing you need to do is make sure you always have access to the current account as keyed by the subdomain.  This means a before filter on any controller that runs under the subdomain that loads in your account.  The idea is that once you load the account, you never have to pull it from the database again:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class AccountSubdomainController &lt; ApplicationController
  before_filter :current_account

protected
  def current_account
    @account ||= params[:current_account] ||= get_account_by_subdomain
    params[:user][:current_account] = @account if params[:user]
    @account
  end

  def get_account_by_subdomain
    Account.where(:subdomain =&gt; request.subdomain.downcase).first
  end
end
</code></pre></div></div>

<p>If you’re not using an account-specific subdomain, just modify this to pull out the account from the URL or something.</p>

<p>Once we have our account, we need to inject the current account into the params to keep it over the whole request.  We also need them in the users subhash if authentication is <em>currently</em> being run.  That means you need to define <code class="language-plaintext highlighter-rouge">current_account</code> in your authentication keys, either in the <code class="language-plaintext highlighter-rouge">devise.rb</code> initializer or on your model’s <code class="language-plaintext highlighter-rouge">devise</code> invocation:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>config.authentication_keys = [:email, :current_account]
</code></pre></div></div>

<p>This tells devise to use not only the <code class="language-plaintext highlighter-rouge">email</code> request parameter but also the <code class="language-plaintext highlighter-rouge">current_account</code> parameter to look up users.  Now we just need to override the lookup method for authentication:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  def self.find_for_database_authentication(conditions)
    acct = conditions[:current_account] || Account.where("users.email" =&gt; conditions[:email]).first
    acct &amp;&amp; acct.users.where(:email =&gt; conditions[:email]).first
  end
</code></pre></div></div>

<p>That’s sufficient for the initial login, but if you stop there your app will authenticate correctly, store the user in the session, but never be able to pull it back out.  We need to a way to get to the <code class="language-plaintext highlighter-rouge">params</code> to pull that <code class="language-plaintext highlighter-rouge">current_account</code> out and provide it to the <code class="language-plaintext highlighter-rouge">user</code> model for a proper lookup.  This is where shit gets a little hacky, because we’re going to create an initializer that overrides how Warden does something:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class Warden::SessionSerializer
  def deserialize(keys)
    klass_name, *args = keys

    # add current account into mix so we don't have to pull it from the db again!
    args &lt;&lt; params[:current_account] if params[:current_account]

    begin
      klass = ActiveSupport::Inflector.constantize(klass_name)
      if klass.respond_to? :serialize_from_session
        klass.serialize_from_session(*args)
      else
        Rails.logger.warn "[Devise] Stored serialized class #{klass_name} seems not to be Devise enabled anymore. Did you do that on purpose?"
        nil
      end
    rescue NameError =&gt; e
      if e.message =~ /uninitialized constant/
        Rails.logger.debug "[Devise] Trying to deserialize invalid class #{klass_name}"
        nil
      else
        raise
      end
    end
  end
end
</code></pre></div></div>

<p>That’s the link between the <code class="language-plaintext highlighter-rouge">current_account</code> in the request and our <code class="language-plaintext highlighter-rouge">user</code> model.  Now we just need a way to use it, and instead of using <code class="language-plaintext highlighter-rouge">self.find</code> like that old gist, we’d be better off implementing this on our model:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def self.serialize_from_session(*args)
  key, salt, account = args
  single_key = key.is_a?(Array) ? key.first : key
  account.users.find single_key
end
</code></pre></div></div>

<p>You should be all set now.  Note that this hack has not been well tested, but I thought it was high time somebody shared a different approach.  Please advise if you have criticisms or a better way.</p>
]]></content>
</entry>
</feed>
