I am using devise.
Before I was able to login without email confirmation.
Then I had to setup email confirmation for my application.
So I added confirmable on user.rb and made changes on devise user migration.
Also, I made changes on devise.rb file.
Also I did setup for mailer as smtp on config/environment/development.rb file.
I think setup is correct for mailer but the problem is that:
When user signs up as new user, mail with confirmation token will goes to that user. But even without clicking that mail link User is able to login.
What is the use of setup for mailer if user can login without confirmation token?
How to prevent user from logging if they don’t verify mail confirmation token?
I have tired making customized confirmable controller but result is same.
application_controller.rb
# frozen_string_literal: true
# Router entry point
require 'json_web_token'
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :make_action_mailer_use_request_host_and_protocol
# before_action :authenticate_user!, :set_mailer_host
protect_from_forgery with: :exception
respond_to :html, :json
def index
render template: 'application'
end
def not_found
render json: { error: 'not_found' }
end
def authorize_request
header = request.headers['Authorization']
header = header.split(' ').last if header
begin
@decoded = JsonWebToken.decode(header)
@current_user = User.find(@decoded[:user_id])
rescue ActiveRecord::RecordNotFound => e
render json: { errors: e.message }, status: :unauthorized
rescue JWT::DecodeError => e
render json: { errors: e.message }, status: :unauthorized
end
end
protected
def current_user
@current_user ||= User.find_by(id: session[:user_id])
end
def signed_in?
!!current_user
end
helper_method :current_user, :signed_in?
def current_user=(user)
session[:user_id] = user&.id
@current_user = user
end
def configure_permitted_parameters
update_attrs = [:password, :password_confirmation, :current_password]
devise_parameter_sanitizer.permit :account_update, keys: update_attrs
devise_parameter_sanitizer.permit(:login, keys: [ :email, :password ])
end
private
def make_action_mailer_use_request_host_and_protocol
ActionMailer::Base.default_url_options[:protocol] = request.protocol
ActionMailer::Base.default_url_options[:host] = request.host_with_port
end
end
authorization_controller.rb
module Api
module V1
class AuthenticationController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :authorize_request, except: :login
# POST /auth/login
def login
@user = User.find_by_email(params[:email])
if @user&.valid_password?(params[:password])
token = JsonWebToken.encode(user_id: @user.id)
time = Time.now + 24.hours.to_i
render json: { token: token, exp: time.strftime("%m-%d-%Y %H:%M"),
username: @user.username, user_id: @user.id }, status: :ok
else
render json: { error: 'unauthorized' }, status: :unauthorized
end
end
private
def login_params
params.permit(:email, :password)
end
end
end
end
users_controller.rb
module Api
module V1
class UsersController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :authorize_request, except: :create
# GET /users
def index
@users = User.all
render json: @users, status: :ok
end
def create
# render plain: params.inspect
@user = User.new(user_params)
# render plain: user_params.insp
if @user.save
render json: @user, status: :created
else
render json: { errors: @user.errors.full_messages },
status: :unprocessable_entity
end
end
def update
user = User.find(params[:id])
if user.update(user_params)
render json: user, status: :created
else
render json: { errors: user.errors.full_messages },
status: :unprocessable_entity
end
end
def show
user = User.find(params[:id])
if !user.nil?
render json: user, status: :ok
else
render json: {errors: user.errors.full_messages}, status: :unprocessable_entity
end
end
def destroy
user = User.find(params[:id])
if user.destroy
render json: {success: "deleted successfully"}, status: :ok
else
render json: {errors: user.errors.full_messages}, status: :not_acceptable
end
end
private
def find_user
@user = User.find_by_username!(params[:_username])
rescue ActiveRecord::RecordNotFound
render json: { errors: 'User not found' }, status: :not_found
end
def user_params
params.permit(
:first_name, :last_name, :username, :email, :password, :password_confirmation
)
end
end
end
end
registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
skip_before_action :require_no_authentication
def update_resource(resource, params)
if resource.encrypted_password.blank?
resource.email = params[:email] if params[:email]
if !params[:password].blank? && params[:password] == params[:password_confirmation]
resource.password = params[:password]
resource.save
end
if resource.valid?
resource.update_without_password(params)
end
else
resource.update_with_password(params)
end
end
end
user.rb
class User < ApplicationRecord
# has_secure_password
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :omniauthable, password_length: 8..36
has_many :identities
has_one :testimonials
has_many :questions
has_many :answers
def facebook
identities.where(provider: 'facebook').first
end
def facebook_client
@facebook_client ||= Facebook.client(access_token: facebook.accesstoken)
end
def twitter
identities.where(provider: 'twitter').first
end
def twitter_client
@twitter_client ||= Twitter.client(access_token: twitter.accesstoken)
end
def google_oauth2
identities.where(provider: 'google_oauth2').first
end
def google_oauth2_client
unless @google_oauth2_client
@google_oauth2_client = Google::APIClient.new(application_name: '',
application_version: '')
@google_oauth2_client.authorization.update_token!(access_token: google_oauth2.accesstoken,
refresh_token: google_oauth2.refreshtoken)
end
@google_oauth2_client
end
#validation for users
validates :username, presence: true, uniqueness: {case_sensitive: false}
# validates_format_of :username, with: /^[a-zA-Z0-9_.]*$/, :multiline => true
validates :email, presence: true, uniqueness: {case_senstive: false}
PASSWORD_FORMAT = /A
(?=.{8,}) # Must contain 8 or more characters
(?=.*d) # Must contain a digit
(?=.*[a-z]) # Must contain a lower case character
(?=.*[A-Z]) # Must contain an upper case character
(?=.*[[:^alnum:]]) # Must contain a symbol
/x
validates :password,
presence: true,
# length: { in: Devise.password_length },
format: { with: PASSWORD_FORMAT, message: 'must contain 8 Characters with at least One Lowercase, One Uppercase, One Number and One Special Character' },
confirmation: true,
on: :create
validates :password_confirmation,
presence: true
validates :password,
# allow_nil: true,
# length: { in: Devise.password_length },
format: { with: PASSWORD_FORMAT, message: 'must contain 8 Characters with at least one Uppercase, One Number and One Special Character' },
confirmation: true,
on: :update
end
2
Answers
There was not any mistake with logic on above code. I had mistake on views which routes generates link.
THE CORRECT url is:
Take a look at your devise initializer, you need to set
allow_unconfirmed_access_for
to 0 (actually, it should be zero by default). According to devise documentation:Devise is using this method to verify unconfirmed access.
Could you also post the results of this methods:
User.find(id_of_user_that_was_just_created).confirmation_required?
User.find(id_of_user_that_was_just_created).confirmed?
User.find(id_of_user_that_was_just_created).confirmation_period_valid?