> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appsignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Impersonation de usuário na autenticação do Rails 8

> Permita que um administrador veja a aplicação como outro usuário, sem criar uma sessão para esse usuário, estendendo o gerador de autenticação do Rails 8.

export const YouTube = ({id, title, description, presenter, duration, republished}) => {
  const playlistId = "PLFHQSOKTqHXA";
  const playlistUrl = `https://www.youtube.com/playlist?list=${playlistId}`;
  if (!id || id === "YOUTUBE_ID") {
    return <div style={{
      border: "1px dashed currentColor",
      borderRadius: "0.5rem",
      opacity: 0.7,
      padding: "2rem 1.5rem",
      textAlign: "center",
      fontSize: "0.875rem"
    }}>
        <strong>Video not published yet.</strong>
        <br />
        {title ? `"${title}" has no YouTube id.` : "This tutorial has no YouTube id."}{" "}
        Replace <code>YOUTUBE_ID</code> in this page with the id from the
        video's URL, once it is in the{" "}
        <a href={playlistUrl}>GoRails x AppSignal playlist</a>.
      </div>;
  }
  const prettyDate = republished ? new Date(`${republished}T00:00:00Z`).toLocaleDateString("en-US", {
    year: "numeric",
    month: "long",
    day: "numeric",
    timeZone: "UTC"
  }) : null;
  const schema = {
    "@context": "https://schema.org",
    "@type": "VideoObject",
    name: title,
    embedUrl: `https://www.youtube-nocookie.com/embed/${id}`,
    contentUrl: `https://www.youtube.com/watch?v=${id}`,
    thumbnailUrl: [`https://i.ytimg.com/vi/${id}/maxresdefault.jpg`],
    creator: {
      "@type": "Organization",
      name: "GoRails",
      url: "https://gorails.com"
    },
    publisher: {
      "@type": "Organization",
      name: "AppSignal",
      url: "https://appsignal.com"
    }
  };
  if (description) schema.description = description;
  if (duration) schema.duration = duration;
  if (republished) schema.uploadDate = republished;
  if (presenter) schema.author = {
    "@type": "Person",
    name: presenter
  };
  return <div style={{
    marginBottom: "1.5rem"
  }}>
      <iframe width="100%" height="450" src={`https://www.youtube-nocookie.com/embed/${id}?list=${playlistId}`} title={title} style={{
    borderRadius: "0.5rem",
    border: 0,
    display: "block"
  }} allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen />
      <p style={{
    fontSize: "0.875rem",
    marginTop: "0.5rem"
  }}>
        <a href={`https://www.youtube.com/watch?v=${id}&list=${playlistId}`}>{`Watch "${title}" on YouTube`}</a>
      </p>
      <script type="application/ld+json" dangerouslySetInnerHTML={{
    __html: JSON.stringify(schema)
  }} />
    </div>;
};

<YouTube id="Nc5CmlQD9xQ" title="Adicionar impersonation à autenticação do Rails 8" description="Permita que um administrador veja a aplicação como outro usuário, sem criar uma sessão para esse usuário, estendendo o gerador de autenticação do Rails 8." presenter="Chris Oliver" duration="PT16M8S" republished="2026-09-19" />

Apresentado por Chris Oliver para o [GoRails](https://gorails.com). Republicado no AppSignal em 19 de setembro de 2026.

A impersonation permite que alguém do suporte veja a aplicação como um usuário específico a vê. Existem gems para isso, mas a maioria é anterior ao `ActiveSupport::CurrentAttributes` e não se encaixa na autenticação que o Rails 8 gera.

Construir isso por conta própria acaba levando cerca de 30 linhas. Isso porque o código gerado é pequeno o suficiente para ler e estruturado para ser estendido.

<h2 id="what-this-covers">
  O que este tutorial cobre
</h2>

* Sobrescrever `Current.user` para que a impersonation percorra o código existente.
* Manter `true_user` disponível para autorização e exibição.
* A restrição de ordem em `resume_session` que, caso contrário, bloquearia seu acesso.

<h2 id="requirements">
  Requisitos
</h2>

| O que | Valor |
| - | - |
| Rails | 8.0 ou posterior |
| Gerado por | `bin/rails generate authentication` |

<h2 id="how-the-generated-code-works">
  Como o código gerado funciona
</h2>

Dois arquivos importam.

`app/models/current.rb` guarda o estado por requisição. Ele armazena a sessão e delega `user` a ela:

```ruby theme={null}
class Current < ActiveSupport::CurrentAttributes
  attribute :session
  delegate :user, to: :session, allow_nil: true
end
```

`app/controllers/concerns/authentication.rb` atribui essa sessão. `require_authentication` chama `resume_session`, que encontra o registro de sessão a partir de um cookie assinado. Quando não retorna nada, a requisição é enviada para a página de login.

O cookie é assinado, não criptografado, então um usuário pode ler o ID da sua sessão, mas não pode alterá-lo.

<h2 id="override-current-user">
  Sobrescrever Current.user
</h2>

Tudo na aplicação já pergunta a `Current.user` quem está autenticado. Em vez de ensinar impersonation a todo esse código, intercepte-a em um único lugar.

Substitua a delegação por um método explícito:

```ruby theme={null}
class Current < ActiveSupport::CurrentAttributes
  attribute :session
  attribute :impersonated_user

  def user
    impersonated_user || session&.user
  end

  def true_user
    session&.user
  end
end
```

`Current.user` agora retorna o usuário impersonado quando um está definido, e o usuário autenticado caso contrário. Sem nenhum dos dois, retorna `nil`, e a requisição é tratada como não autenticada, que é exatamente o que o restante do concern espera.

`true_user` não é opcional. Você precisa dele para o banner que informa a um administrador quem ele está impersonando. Você também precisa dele para a autorização.

<h2 id="add-the-impersonation-methods">
  Adicionar os métodos de impersonation
</h2>

Em `app/controllers/concerns/authentication.rb`:

```ruby theme={null}
included do
  helper_method :impersonating?
end

def impersonating?
  Current.impersonated_user.present?
end

def impersonate_user(user)
  Current.impersonated_user = user
  session[:impersonated_user_id] = user.id
end

def stop_impersonating
  Current.impersonated_user = nil
  session.delete(:impersonated_user_id)
end

def find_impersonated_user
  return unless session[:impersonated_user_id]

  User.find_by(id: session[:impersonated_user_id])
end
```

`Current` é limpo após cada requisição, então o usuário impersonado precisa ser encontrado novamente a cada uma delas. A sessão do cookie carrega o ID entre as requisições. A guard clause em `find_impersonated_user` evita uma consulta por um usuário que não pode existir.

Note o que está deliberadamente ausente: nenhum registro de `Session` é criado. Impersonation não é um login. Se você criasse um, o cliente veria uma segunda sessão ativa na conta dele, o que seria preocupante e impreciso.

<h2 id="wire-it-into-resume_session">
  Integrar ao resume\_session
</h2>

```ruby theme={null}
def resume_session
  Current.impersonated_user = find_impersonated_user
  Current.session = find_session_by_cookie
end
```

<Warning>
  Estas duas linhas precisam estar nesta ordem. `require_authentication` usa o valor de retorno de `resume_session` para decidir se redireciona para o login. Atribuir o usuário impersonado por último faz dele o valor de retorno, que é `nil` sempre que ninguém está sendo impersonado. Toda requisição seria então redirecionada para o login, e o login pareceria estar quebrado.
</Warning>

Pare também a impersonation quando a sessão terminar. Adicione `stop_impersonating` a `terminate_session`. Sem isso, sair e entrar novamente retoma a impersonation de quem você estava impersonando antes. O ID ainda está na sessão do cookie.

<h2 id="add-the-controller">
  Adicionar o controller
</h2>

```ruby theme={null}
# config/routes.rb
resource :impersonate, only: [:create, :destroy]
```

```ruby theme={null}
class ImpersonatesController < ApplicationController
  before_action :require_admin

  def create
    impersonate_user(User.find(params[:id]))
    redirect_to root_url
  end

  def destroy
    stop_impersonating
    redirect_to root_url
  end

  private

  def require_admin
    redirect_to root_url unless Current.true_user&.admin?
  end
end
```

<Warning>
  `require_admin` verifica `Current.true_user`, não `Current.user`. Assim que a impersonation está ativa, `Current.user` é o usuário impersonado. Verificá-lo testaria se a pessoa impersonada é administradora. Um administrador impersonando um usuário comum falharia na sua própria verificação de autorização e não conseguiria acessar `destroy` para parar. As decisões de autorização pertencem a `true_user` em todo o fluxo.
</Warning>

<h2 id="show-it-in-the-layout">
  Exibir no layout
</h2>

```erb theme={null}
<% if impersonating? %>
  Viewing as <%= Current.user.email_address %>
  (signed in as <%= Current.true_user.email_address %>)
  <%= button_to "Stop impersonating", impersonate_path, method: :delete %>
<% end %>
```

Declare a impersonation em palavras. Um banner que se distingue apenas pela cor não avisa a todos que estão olhando a conta de outra pessoa.

<h2 id="attributing-impersonated-activity">
  Atribuindo atividade impersonada
</h2>

A impersonation cria uma lacuna na sua telemetria. Todo trace registra o usuário impersonado, então a atividade de suporte fica indistinguível da atividade do próprio cliente.

Feche essa lacuna [marcando com tags](/guides/tagging) as requisições com o ator real:

```ruby theme={null}
Appsignal.add_tags(
  impersonating: impersonating?,
  true_user_id: Current.true_user&.id
)
```

As tags fazem duas coisas. Erros gerados durante a impersonation podem ser diferenciados dos [erros](/errors) que seus clientes encontram. Uma sessão de suporte não parece então um pico de falhas voltadas ao cliente. E quando um cliente pergunta por que um registro mudou, o trace registra quem estava por trás da mudança.

A impersonation também é uma boa candidata para uma trilha de auditoria própria. O AppSignal é monitoramento, não um log de auditoria. Trate as tags como um recurso de diagnóstico, e mantenha o registro oficial no seu próprio banco de dados.

<h2 id="transcript">
  Transcrição
</h2>

Transcrito do vídeo e levemente editado. As legendas automáticas entenderam errado vários nomes de produtos e de APIs, e esses foram corrigidos.

<Accordion title="Ler a transcrição">
  **0:02** E aí, pessoal, neste episódio vamos adicionar impersonation ao gerador de autenticação do Rails 8. Na verdade, é bem mais simples do que você imaginaria. Então vamos direto ao ponto. Vou rodar `rails generate authentication` para criar os modelos de user e session e todos os controllers e o módulo de autenticação que cuida da maior parte do trabalho. E vamos rodar `rails db:migrate` para criar essas tabelas no banco de dados.

  **0:31** Depois vou rodar o console do Rails, e vamos criar dois usuários. Vamos fazer login como um, vamos impersonar o outro. Então `User.create!`, com o endereço de e-mail [chris@gorails.com](mailto:chris@gorails.com) e a senha "password". Depois fazemos a mesma coisa, mas com outro usuário com um endereço de e-mail diferente.

  **0:51** Vou colocar qualquer senha nele, e isso já deve bastar. Então vamos dar uma olhada rápida na própria autenticação. Vamos em `application.html.erb` e escrevemos: se `authenticated?`, então podemos exibir `current_user.email_address`. Isso é o que veremos para o usuário atualmente logado, e queremos que isso mude quando estivermos impersonando um usuário diferente. Também vamos ter um botão para sair, que vai para o caminho da session com o método `delete` para nos deslogar.

  **1:34** Ok, isso está bom. Devemos conseguir rodar nosso servidor Rails e abrir isso no navegador. Aí está meu teste anterior que eu tinha configurado. E aqui, `sessions/new` é para onde vamos ser levados. Quando vamos para a URL raiz, seremos redirecionados para cá.

  **1:52** Eu já criei uma URL raiz principal para nós. Então ela nos leva para a autenticação, que é o que acontece por padrão. Toda página é autenticada, e você precisa dizer explicitamente que uma página não precisa de autenticação. Aqui podemos fazer login como [chris@gorails.com](mailto:chris@gorails.com), e pronto. [chris@gorails.com](mailto:chris@gorails.com), posso sair e entrar de novo.

  **2:16** Vamos fazer isso mais uma vez para garantir que tudo funciona. Então, como fazemos autenticação com impersonation? Vamos dar uma olhada rápida na classe `Current`. É ela que cuida de armazenar a session e o usuário atuais para a requisição. Isso é limpo depois que a requisição termina, e simplesmente atribuímos a session e obtemos o usuário através dela.

  **2:44** O módulo de autenticação é o responsável por atribuir essa session. Então, com `require_authentication`, ele vai chamar esse método, que vai retomar a session atual ou pedir que você se autentique. Retomar é tão simples quanto encontrar a session pelo cookie, o que vai buscar o registro de session no banco de dados com base no ID definido nos seus cookies. Eles são assinados para não poderem ser adulterados, mas continuam acessíveis para os usuários lerem, porque isso não importa, já que eles não conseguem fazer nada com isso para abusar. Então, para a impersonation, basicamente precisamos adicionar algo para interceptar isso.

  **3:29** E quando pegamos o usuário atual, na verdade obtemos o usuário que estamos impersonando, não aquele com o qual estamos logados. E eu também não quero necessariamente criar uma session para ele, porque não estamos realmente logados como ele. Estamos só tentando fazer suporte ao cliente, por exemplo. Então vamos falar sobre como queremos que isso funcione. Em `current.rb`, esse `delegate :user` é exatamente o que precisamos interceptar.

  **3:54** Se pensarmos dessa forma, poderíamos ter um atributo para o usuário impersonado, que atribuiríamos, e depois um método para `user` que retornaria o usuário impersonado ou o usuário da session. E se não houvesse session nem usuário impersonado, isso retornaria `nil`, e não estaríamos autenticados de forma alguma. Então isso funcionaria bem e faria com que também funcionasse com o módulo de autenticação. Então não precisamos mais realmente daquela linha que delega `user`, porque de qualquer forma vamos precisar basicamente sobrescrever isso aqui. Agora, poderíamos claro adicionar um método como `true_user` se quiséssemos, dizendo que esse é o usuário da session, e sempre conseguir pular o usuário impersonado, porque queremos que o usuário impersonado se comporte como nosso usuário atual, mas podemos precisar exibir um banner e mostrar o usuário original, para o qual você poderia adicionar um pequeno helper aqui também.

  **5:01** Então isso é opcional, não necessariamente algo que precisamos, mas vamos entrar no nosso `application.html.erb` e adicionar algo que diga: se estiver impersonando, queremos ter um botão para parar de impersonar. Vamos precisar de algum tipo de caminho `impersonate` com o método `delete` também para parar a impersonation, e então podemos ter um botão para impersonar um usuário, que vai para o caminho `impersonate`, mas passaríamos um ID de um usuário. Isso provavelmente precisaria ser dinâmico na sua área de administração, e vamos fazer isso como uma requisição `POST` porque queremos criar a session de impersonation ali. Então precisamos implementar um punhado de métodos e um controller para conseguir fazer isso. Então vamos até nossas routes.

  **5:58** Vamos adicionar um resource para `impersonate`. Podemos editar `app/controllers/impersonates_controller.rb` e criar esse `ImpersonatesController`, que vai herdar de `ApplicationController`. Uma coisa que queremos fazer é adicionar um `require_admin` aqui. Não temos uma flag de admin nos nossos usuários, mas isso é meio que um exemplo do que você poderia fazer.

  **6:27** Então você poderia dizer `require_admin`, e isso verificaria se `current_user` é admin, e se você não for um admin, redireciona para o caminho ou URL raiz, o que você quiser fazer. Talvez você queira colocar um alerta ali ou algo assim, mas aqui precisamos criar nossas duas actions. Uma vai criar a session de impersonation, e a outra vai destruí-la. Isso vai ser algo como `stop_impersonating`, e vamos te redirecionar para a URL raiz, e para `create` vai ser bem simples, bem parecido: vamos dizer `impersonate_user(User.find(params[:id]))`.

  **7:13** Então você vai passar um ID na URL. Vamos então impersonar esse usuário e te redirecionar para a página inicial, então você vai ver a página inicial agora como esse usuário. Agora precisamos ir no módulo de autenticação e adicionar esses métodos. Então, se olharmos para esses outros métodos, podemos construir algo parecido. Precisamos do método `impersonating?`, que vai retornar verdadeiro ou falso.

  **7:45** Precisamos de um `impersonate_user`, que vai configurar isso. Precisamos de um `stop_impersonating`. Não precisa de nada além disso. Mas também vamos precisar de um `find_impersonated_user`. Esse é o que vamos usar depois que você impersonar e for redirecionado para a próxima página.

  **8:07** Essa requisição vai precisar encontrar o usuário impersonado, assim como encontra sua session atual com base no cookie. Então vamos começar simples. Vamos dizer: `Current.impersonated_user` está presente? Sim, você está impersonando.

  **8:28** Não, você não está. Então, quando quisermos impersonar um usuário, vamos atribuir essa variável, `impersonated_user` igual a esse usuário. E também precisamos definir algo para que o ID de `impersonated_user` seja mantido para a próxima requisição. Vamos usar a session para isso. E na próxima requisição podemos verificar se há um ID na session para o ID do usuário impersonado.

  **8:58** Se houver, podemos dizer `find_by` com o ID e tentar buscar esse usuário. Do jeito que estou configurando aqui, estamos verificando se esse ID está presente, e assim podemos evitar totalmente consultar o Active Record se não houver ID. Não precisamos consultar o banco de dados por um usuário cujo ID é `null`, porque não vamos obter nenhum usuário de volta para isso de qualquer forma. Então não faz sentido nem fazer essa consulta. Então vamos retornar o usuário ou `nil`, e então `stop_impersonating` é bem simples.

  **9:29** O usuário impersonado agora deve ser `nil`, e queremos remover `impersonated_user_id` da session. Bem simples. Então esses são nossos principais itens de impersonation. Vou adicionar mais uma linha em branco aqui. Percebi que é assim que o módulo de autenticação já está organizado.

  **9:51** Alguma organização com linhas em branco extras ali. E então precisamos integrar isso agora ao processo de autenticação de fato. E o principal processo de autenticação é esse método `require_authentication`: ou retoma uma session, ou reinicia uma nova. E envia o usuário para a página de login. Então `resume_session` é onde realmente precisamos modificar.

  **10:10** Então vamos deixar `Current.impersonated_user` igual a `find_impersonated_user`. E depois vamos definir a session. É importante que essas duas linhas estejam nessa ordem específica, porque o valor de retorno de `resume_session` é o que determina se você é redirecionado para o login ou não. Se você colocasse isso depois, e não estivesse impersonando, continuaríamos te enviando para `require_authentication`, porque `impersonated_user` seria `nil`. Esse método retornaria `nil` aqui, o que é falsy, o que então dispara esse comportamento.

  **10:48** E pareceria que você nunca consegue fazer login. Então é importante colocar essas coisas corretamente, para que o valor de retorno de `resume_session` esteja configurado direito. E se tudo correr bem, a única outra coisa de que realmente precisamos é um helper method para `impersonating?`, para que possamos usá-lo nas nossas views para exibir esse botão. Depois você provavelmente também vai querer verificar se o usuário é admin ou algo assim. E acabei de perceber que precisamos garantir que temos o ID do usuário como chave sempre que acessamos a session.

  **11:27** Queremos garantir que isso seja consistente. Caso contrário, você vai ter problemas estranhos. Além disso, quando estamos encerrando a session, realmente queremos garantir que também paramos a impersonation, porque quando você sai e entra de novo, você não quer acabar logado na sua própria conta, mas automaticamente impersonando quem quer que você estivesse impersonando por último. Isso seria estranho. Então queremos garantir que paramos a impersonation também quando você sai.

  **11:56** Então vamos testar isso. Vamos para o navegador. Estamos em `sessions/new`. Podemos fazer login, [chris@gorails.com](mailto:chris@gorails.com). Isso ainda nos loga, o que é bom.

  **12:05** Não quebramos nada. Não estamos impersonando, então o botão de impersonar aparece. Podemos clicar nele. Isso vai fazer uma requisição `POST` para o controller de impersonation, que vai chegar em `create`. Vai ter o ID 2.

  **12:20** Temos isso na URL para isso. Você também poderia configurar isso em `users/:id/impersonate` se quiser. Eu só coloquei tudo em um controller e você passa o ID. Dos dois jeitos funciona, mas conseguimos impersonar. E agora, `Current.user.email_address` é [bob@bob.bob](mailto:bob@bob.bob).

  **12:37** Mas ainda estamos logados como [chris@gorails.com](mailto:chris@gorails.com). Então agora sabemos que estamos impersonando, porque vemos `stop_impersonating`. Se quisermos abrir o console do Rails, deixa eu aumentar a fonte. Podemos dizer `Session.count`. E o que veremos é que há apenas uma session para mim, [chris@gorails.com](mailto:chris@gorails.com).

  **13:02** Se dissermos `Session.first` e pegarmos o usuário, vai ser o usuário número um, que não estou mostrando aqui. Está filtrado, mas o endereço de e-mail é [chris@gorails.com](mailto:chris@gorails.com). Então só eu estou logado, e minha impersonation não está sendo armazenada como uma session, o que seria estranho se um cliente visse que outra pessoa está logada na conta dele. Então não queremos realmente criar uma session para isso. Mas você poderia, se quisesse.

  **13:31** E também marcar isso no registro. Você poderia adicionar algum campo extra ao modelo `Session` para manter um histórico dessas sessions de impersonation também. Tem muita coisa que você pode expandir a partir disso. Então agora, se pararmos de impersonar, isso vai definir `Current.impersonated_user` como `nil`, mas também remover `impersonated_user_id` da session. E voltaremos para [chris@gorails.com](mailto:chris@gorails.com).

  **13:58** E posso trocar para [bob@bob.bob](mailto:bob@bob.bob) a qualquer momento. E isso na verdade só exigiu modificar `resume_session` e `terminate_session` no final para incluir nossas mudanças de impersonation. E no nosso `current.rb`, em vez de fazer a delegação, vamos usar o usuário impersonado. E claro, sempre temos acesso a `true_user` também. Então, se formos no nosso `application.html.erb`, poderíamos dizer, se estiver impersonando, você provavelmente vai ter o endereço de e-mail do usuário atual ali.

  **14:39** Mas talvez, se você estiver impersonando, você diria `Current.true_user.email_address`, se quiser mostrar o usuário real que você é. Agora veremos [chris@gorails.com](mailto:chris@gorails.com) aparecer nesse lugar. Então você tem acesso aos dois tipos de usuário, assim como com a gem Pretender para Devise ou qualquer outra biblioteca de impersonation que existe para autenticação tradicional. Mas elas não funcionam com `CurrentAttributes`. E por isso é bem legal a gente conseguir construir isso sozinho com só um pouco de código.

  **15:18** Não tem muita coisa aqui. E é fácil o suficiente de entender enquanto você olha e descobre como isso funciona. Então estou realmente satisfeito com como isso ficou. A autenticação do Rails 8 é muito, muito flexível. Você pode fazer todo tipo de coisa com ela e personalizá-la como quiser, seja para impersonation, OAuth, qualquer coisa.

  **15:39** Tem todo tipo de flexibilidade ali. E é muito, muito fácil de ler com esse módulo de autenticação. Então é isso para este episódio. Espero que tenham gostado. E se vocês quiserem ver mais coisas assim, deixem saber nos comentários abaixo.

  **15:56** E vamos usar isso como inspiração para nossas próximas lições. É isso. Um abraço. E a gente se fala depois. Até mais.
</Accordion>

<h2 id="related-tutorials">
  Tutoriais relacionados
</h2>

* [params.expect](/tutorials/ruby/params-expect)
* [Depurando com a gem debug](/tutorials/ruby/debugging-with-the-debug-gem)

<h2 id="about-this-tutorial">
  Sobre este tutorial
</h2>

Este tutorial resume um screencast do GoRails sobre como adicionar impersonation à autenticação do Rails 8, por Chris Oliver. O screencast é a obra original. O GoRails o publica, junto com o restante da série, em [gorails.com](https://gorails.com).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.