Skip to content
jrnk edited this page Apr 11, 2020 · 34 revisions

In this tutorial we will build upon the app created at Simple Password Authentication so make sure you understand it.

First add some db fields:

ActiveRecord

rails g sorcery:install user_activation  --only-submodules

Which will create:

class SorceryUserActivation < ActiveRecord::Migration
  def change
    add_column :users, :activation_state, :string, :default => nil
    add_column :users, :activation_token, :string, :default => nil
    add_column :users, :activation_token_expires_at, :datetime, :default => nil

    add_index :users, :activation_token
  end
end
rake db:migrate

Mongoid

For mongoid just add these three fields to your User model :

field :activation_state,            type: String
field :activation_token,            type: String
field :activation_token_expires_at, type: DateTime

Note that the token_expires_at field is only needed if you have setup user.activation_token_expiration_period in the sorcery initializer.

In addition you can add an index on the token field :

index({ activation_token: 1 }, { unique: true, background: true })
$ rake db:mongoid:create_indexes

Now our database is ready, we need a mailer with two actions:

$ rails g mailer UserMailer activation_needed_email activation_success_email

You really don't have to use ActionMailer. You can use any ruby object that responds to the above actions. Actually these method names are configurable too! This is useful in case you want to send emails in the background, with gems such as 'delayed_job'. Simply pass your own 'mailer' and make it create the background jobs as you normally would, when triggered by Sorcery.

We need to edit the mailer and add a 'user' parameter to each action because sorcery will send each action the new user as a parameter:

# app/mailers/user_mailer.rb
def activation_needed_email(user)
...

def activation_success_email(user)
...

Then add the user_activation submodule:

# config/initializers/sorcery.rb
Rails.application.config.sorcery.submodules = [:user_activation, bla, bla, ...]

And configure the User model with our mailer defined:

# config/initializers/sorcery.rb
Rails.application.config.sorcery.configure do |config|
  ...
  config.user_config do |user|
    ...
    user.user_activation_mailer = UserMailer
    ...
  end
end

Add the routes accordingly:

# config/routes.rb
resources :users do
  member do
    get :activate
  end
end

Now when a user is created, an email will be sent to him. Currently it's not a very informative email, but we will fix that:

# app/mailers/user_mailer.rb
def activation_needed_email(user)
  @user = user
  @url  = activate_user_url(@user.activation_token)
  mail(to: user.email, subject: 'Welcome to My Awesome Site')
end

def activation_success_email(user)
  @user = user
  @url  = login_url
  mail(to: user.email, subject: 'Your account is now activated')
end
# app/views/user_mailer/activation_needed_email.text.erb
Welcome to example.com, <%= @user.email %>
===============================================

You have successfully signed up to example.com,
your username is: <%= @user.email %>.

To login to the site, just follow this link: <%= @url %> .

Thanks for joining and have a great day!

Here is the equivalent in html:

# app/views/user_mailer/activation_needed_email.html.erb
<!DOCTYPE html>
<html>
  <head>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
  </head>
  <body>
    <h1> Welcome <%= @user.email %> </h1>
    <p>
      You have successfully signed up to www.example.com, and your username is: <%= @user.email %>.
    </p>
    To login to the site, just follow this link: <%= @url %> .
    <p>
    </p>
    <p>Thanks for joining and have a great day!</p>    
  </body>
</html>
# app/views/user_mailer/activation_success_email.text.erb
Congratulations, <%= @user.email %>!

You have successfully activated your example.com account,
your username is: <%= @user.email %>.

To login to the site, just follow this link: <%= @url %>.

Thanks for joining and have a great day!

Nice. The user will get an activation URL, that won't work because we didn't add the code for it. Let's add a controller action that will activate users:

# app/controllers/users_controller.rb
skip_before_action :require_login, :only => [:index, :new, :create, :activate]

def activate
  if @user = User.load_from_activation_token(params[:id])
    @user.activate!
    redirect_to(login_path, :notice => 'User was successfully activated.')
  else
    not_authenticated
  end
end

@user.activate! will make the user active, send a success email and clear the activation code.

If you don't want a success email, in the sorcery configuration of the model, set activation_success_email_method_name to nil.

You can set various other options for this submodule. See the docs for details.

To resend the activation email after user changes email address, add following callbacks to the user model:

# app/models/user.rb
before_update :setup_activation, if: -> { email_changed? }
after_update :send_activation_needed_email!, if: -> { previous_changes["email"].present? }
Clone this wiki locally