Custom error message for invalid confirmation token in Devise for Rails

2.1k Views Asked by At

tl;dr:

Is there a non-hacky way to get a custom full error message for just one attribute (confirmation_token) and error (:invalid) combination on a single model (User in this case)? Or to override the message using Devise config?

Context

I'm working on a project in Rails 4 and I've been asked to change the error message Devise uses to tell the user that a registration confirmation token (part of a link included in the registration confirmation email) is invalid.

Devise has a nice Yaml config file to edit error messages, but this error does not seem to appear there. Looking into the code for Devise, it just creates a standard ':invalid' error on the confirmation_token field of the user model, rendered as a normal active record error (using errors.full_messages).

https://github.com/plataformatec/devise/blob/8e5c098e3aa53d6a72faadd8b929abaf2bb6d496/lib/devise/models/authenticatable.rb

(The method call resolves to find_or_initialize_with_errors with ':invalid' set as a default.)

I could monkey-patch Devise or fork the project, but that should be a last resort. Especially for what's basically a copy change.

I looked into using en.yml in the config/locales folder to set a custom message. Unfortunately, according to the Rails docs the error message format seems to be set globally:

https://github.com/rails/rails/blob/master/activemodel/lib/active_model/locale/en.yml#L4

so unless I'm mistaken, if I want to avoid starting the error message with 'Confirmation token' then I have to find all the possible full error messages that could be returned by any form submission and ensure they still make sense. Most would then say unhelpful things like " is invalid" or " cannot be blank".

Needless to say there are a lot of forms in this project, so that solution is out of scope for this bug-fix.

I could give the attribute a human name of "" or "The" or something I suppose, but that could have a knock on effect elsewhere so is best avoided.

I've looked in vain for a way to limit the scope of the error message format string to a single attribute or model.

Presumably the "Rails way" is to override the full message in the validator, but that code is buried away in the gem in this case. The only ways I can think to get around this issue are hacky or fragile.

1

There are 1 best solutions below

5
On BEST ANSWER

Ok - here's what I did for now, aside from creating a PR on devise itself:

config/locales/en.yml

  devise:
    confirmations:
      header: 'Resend confirmation instructions'
    failure:
      confirmation_token_invalid: 'The confirmation link is no longer valid.  Please try sending a new confirmation email below.'

app/views/devise/confirmations/new.html.erb

<div id="confirmations-screen">
  <div class="dialog-head">
    <p><%= t('devise.confirmations.header') %></p>
  </div>

  <%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %>
    <% unless resource.errors.empty? %>
      <% resource.errors.full_messages.each do |msg| %>
          <% if msg == 'Confirmation token is invalid' %>
            <%= t('devise.failure.confirmation_token_invalid') %>
          <% else %>
            <%= msg %>
          <% end %>
      <% end %>
    <% end %>

    <label for="email"><%= t('account.email.labels.email') %></label>
    <input type="email" id="user_email" name="user[email]" class="form-control">
    <button><%= t('devise.confirmations.resend_button') %></button>
  <% end %>
</div>