'How to use authenticate_or_request_with_http_token for a http request in Rails?
If use authenticate_or_request_with_http_token in an API application, it works fine as this guide:
https://www.mccartie.com/tech/2016/11/03/token-based-api-authentication.html
But tried it in a normal rails application without json format, not work.
The source as:
# application_controller.rb
class ApplicationController < ActionController::Base
def set_account
current_user = authenticate_token
...
end
def authenticate
authenticate_or_request_with_http_token do |token, options|
ActiveSupport::SecurityUtils.secure_compare(token, 'a-test-token-here')
end
end
end
However, it got this error in an action controller:
AbstractController::DoubleRenderError in PostController#index
Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".
From here, got information that not json api:
http://railscasts.com/episodes/352-securing-an-api?view=comments#comment_164947
Rails version: 5.2.2
Edit
Have changed to these files:
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
end
app/controllers/api_controller.rb
class ApiController < ActionController::Base
def require_login
authenticate_token
end
private
def authenticate_token
authenticate_or_request_with_http_token do |token, options|
Rails.logger.info '------------token---------'
Rails.logger.info token
Rails.logger.info '------------token---------'
ActiveSupport::SecurityUtils.secure_compare(token, 'HrcLNGkq8T7Hc4Kxs8bYQw1z')
end
end
end
app/controllers/post_controller.rb
class PostController < ApiController
before_action :require_login
def index
end
end
Have used post method created an user with token:
app/controllers/sessions_controller.rb
class SessionsController < ApiController
skip_before_action :verify_authenticity_token
skip_before_action :require_login, only: [:create], raise: false
def create
if user = User.valid_login?(params[:email], params[:password])
generate_token(user)
end
end
private
def generate_token(user)
user.regenerate_token
end
end
app/models/user.rb
class User < ApplicationRecord
has_secure_password
has_secure_token
end
DB
User Load (0.4ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<User id: 1, name: "abc", token: "HrcLNGkq8T7Hc4Kxs8bYQw1z", email: "[email protected]", password_digest: "$2a$12$EqrQXjUWZIswSpX5J3BBIp4gsQ8GcpZPJ4eKUS4/oY4...", created_at: "2019-08-22 13:57:57", updated_at: "2019-08-22 14:31:01">
But when access endpoint: http://localhost:3000/post/index, got log:
Started GET "/post/index" for ::1 at 2019-08-22 23:54:11 +0900
Processing by PostController#index as HTML
Rendering text template
Rendered text template (0.0ms)
Filter chain halted as :require_login rendered or redirected
Completed 401 Unauthorized in 1ms (Views: 0.8ms | ActiveRecord: 0.0ms)
and this message in the browser:
HTTP Token: Access denied.
It seems didn't go into the authenticate_or_request_with_http_token block.
Solution 1:[1]
This way works:
# app/controllers/api_controller.rb
class ApiController < ActionController::Base
def require_login
@current_user = User.find_by(id: 1)
authenticate_token
end
private
def authenticate_token
auth = ActionController::HttpAuthentication::Token.encode_credentials(@current_user.token)
request.headers['Authorization'] = auth
# request['authorization'] = "Token token=HrcLNGkq8T7Hc4Kxs8bYQw1z"
authenticate_or_request_with_http_token do |token, options|
Rails.logger.info '------------token---------'
Rails.logger.info token
Rails.logger.info '------------token---------'
if user = User.find_by(token: token)
ActiveSupport::SecurityUtils.secure_compare(token, user.token)
end
end
end
end
Solution 2:[2]
The issue is more that the method you're calling fallbacks to a request method (with a builtin response) on failure:
def authenticate_or_request_with_http_token(realm = "Application", message = nil, &login_procedure)
authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm, message)
end
If you don't want the render response happening, just use authenticate_with_http_token as a drop in replacement. If you do want the render response happening then you need to stop processing on authentication failure.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | 02040402 |
| Solution 2 | toxaq |
