1

Here I use Devise to help me authenticate user. I test my app by simulating this scenario:

  1. Open app in 2 browser tabs (A and B)
  2. In tab A, logging in and open page called "new"
  3. Refresh tab B, and logging out from tab B
  4. Back to tab A then submit form "new" via tab A

But I got error:

ActionController::InvalidAuthenticityToken in ArticlesController#create

instead of redirect user to login page.

Here's my code:

controllers/articles_controller.rb

class ArticlesController < ApplicationController
    before_action :is_logged_in?
    before_action :is_admin?, except: [:index, :show]

    def new
        @article = Article.new
    end

    def index
        @articles = Article.all
    end

    def edit
        @article = Article.find(params[:id])
    end

    def update
        @article = Article.find(params[:id])
        if @article.update(article_params)
            redirect_to @article
        else
            render :edit
        end
    end

    def destroy
        @article = Article.find(params[:id])
        @article.destroy

        redirect_to articles_path
    end

    def create
        @article = Article.new(article_params)

        if @article.save
            redirect_to @article
        else
            render :new
        end
    end

    def show
        @article = Article.find(params[:id])
    end

    private
        def article_params
            params.require(:article).permit(:title, :text)
        end

        def is_admin?
            if !admin_signed_in?
                flash[:notice] = "Access Forbidden!"
                redirect_to root_path
            end
        end

        def is_logged_in?
            if !user_signed_in? && !admin_signed_in?
                flash[:notice] = "Log in first!"
                redirect_to new_user_session_url
            end
        end
end

routes.rb

Rails.application.routes.draw do
  devise_for :admins
  devise_for :users
  root 'welcome#index'
  resources :articles do
    resources :comments
  end
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

views/articles/new.html.erb

<h1>Form Create Article</h1>
<%= render 'form' %>
<%= link_to 'Back', articles_path %>

views/articles/_form.html.erb

<%= form_with model: @article, local: true do |form| %>

  <% if @article.errors.any? %>
    <div id="error_explanation">
      <h2>
        <%= pluralize(@article.errors.count, "error") %> prohibited
        this article from being saved:
      </h2>
      <ul>
        <% @article.errors.full_messages.each do |msg| %>
          <li><%= msg %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <p>
    <%= form.label :title %><br>
    <%= form.text_field :title %>
  </p>

  <p>
    <%= form.label :text %><br>
    <%= form.text_area :text %>
  </p>

  <p>
    <%= form.submit %>
  </p>

<% end %>

How to make my app redirects user to login page in case user submit form when they are not authenticated? Thanks.

Hendro Febrian
  • 202
  • 1
  • 3
  • 12
  • In a normal scenario where you have only one tab, if you are accessing the form page without logging in, then is it redirected to login page? – Pavan Oct 22 '18 at 09:09
  • @Pavan yes, when I refresh the page, it redirects me to login page. The only problem is when I try to submit form with POST method, it returns that token autencity error. Is it possible to just redirect the user to the login page? – Hendro Febrian Oct 22 '18 at 09:13

1 Answers1

1

ActionController::InvalidAuthenticityToken in ArticlesController#create

What is happening?

As you are logged in as same user in both the tabs, in Tab B, when you logged out from the app, a new authenticity token gets updated in the server making the already existing authenticity token in the Tab A as Invalid. So when you submit the form it fails with that exception

But why is_logged_in? check is not passed in the first place when you submit the form in Tab A?

After when you logged out from the app in Tab B, you are submitting the form in Tab A without refreshing it. Now the sessions are still active and valid in Tab A as the cookies are not updated resulting in the is_logged_in? check to fail and allowing the create action to take place.

How to make my app redirects user to login page in case user submit form when they are not authenticated?

In a normal scenario, your code should work fine! But as your scenario is different, you probably need to rescue the InvalidAuthenticityToken error and redirect the user to login page when the form is submitted in Tab A.

Pavan
  • 33,316
  • 7
  • 50
  • 76
  • How to rescue InvalidAuthenticityToken? I am totally beginner with RoR. – Hendro Febrian Oct 22 '18 at 10:58
  • @HendroFebrian You can use the Durrell Chamorro answer in this post https://stackoverflow.com/questions/36509300/gracefully-handling-invalidauthenticitytoken-exceptions-in-rails-4 – Pavan Oct 22 '18 at 11:09
  • 1
    Works like charm! Thank you pavan. Can't be happier. – Hendro Febrian Oct 22 '18 at 11:16
  • one more question pls. As you can see, I have 2 kind of users, the 'user' and 'admin'. But now that 2 types of user can login concurrenty in the same browser window. I want to make sure that only one user is allowed to log in within the same browser window. Is overriding Devise controller is a good practice to do that? Or Devise actually provide a helper to achieve that? Thanks – Hendro Febrian Oct 22 '18 at 11:24