Rails - authenticate_or_request_with_http_basic custom "access denied" message - ruby-on-rails

I'm struggling myself trying to change that default message once you insert invalid credentials as username:password on rails using authenticate_or_request_with_http_basic.
For example if I make a curl request on a method that need this authentication, once I insert the wrong username and password it returns HTTP Basic: Access denied.
So, in this case I would like to be able to customize this message with a specific XML formatted string, (just like twitter API does). Is that possible?
Thanks in advance

If you want to customize the message in the login prompt, just pass the message to the method call.
authenticate_or_request_with_http_basic "My custom message" do |user_name, password|
user_name == USER_NAME && password == PASSWORD
end
If you want to customize the final error message, according to Rails 2.3.4 source code you can do this only for the HTTP Digest authentication.
def authentication_request(controller, realm, message = nil)
message ||= "HTTP Digest: Access denied.\n"
authentication_header(controller, realm)
controller.__send__ :render, :text => message, :status => :unauthorized
end
The Basic Authentication has the error message hard-coded into the method.
def authentication_request(controller, realm)
controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}")
controller.__send__ :render, :text => "HTTP Basic: Access denied.\n", :status => :unauthorized
end

Related

Devise does not show correct error via login

I'm using devise and devise-token-auth for auth in the backend and 'redux-auth` on the client side.
I'm trying to get devise to send a proper error message to show to a client in case the account is temporarily locked
this is my user.rb file
# devise method to check if user is banned
def active_for_authentication?
super && !self.banned?
end
# message to send in case of ban. doesnt work yet
def inactive_message
self.banned? ? :locked : super
end
def banned?
return self.role == 'banned'
end
my devise.en.yml file
en:
devise:
confirmations:
confirmed: "Your email address has been successfully confirmed."
failure:
locked: "Your account is locked."
errors:
messages:
locked: "Your account has been banned"
I seem to be getting a standard error response from devise when trying to login either with redux-auth or CURL (i cant seem to find the given message anywhere in devise.en.yml file)
curl http://localhost:3000/auth/sign_in -d "email=foo#foo.com&password=123"
the response
{"success":false,"errors":["A confirmation email was sent to your account at 'foo#foo.com'."]}
Where can i customize the message?
The message is in the devise_token_auth yaml file: https://github.com/lynndylanhurley/devise_token_auth/blob/123cfb730ebaf4e2acc124edd39e68816cb0b8cf/config/locales/en.yml
so you need to override it by adding the following to one of your local yaml files:
en:
devise_token_auth:
sessions:
not_confirmed: "Custom message"

Use API to authenticate in Rails

I currently have a Rails application that is connected to an existing SQL database. I am using Devise for my user management, however the pre-existing User table in the database uses a very customized password encryption method.
There is a web service I can connect to that passes a JSON object with the login information to authenticate whether it is valid or not, and I have to manage my own session and everything after that.
I attempted to follow "Railscast #250", and combine it with Devise and some Stack Overflow searches, but things are not going very well.
This is what I have now, but it isn't doing anything, and I just don't feel like I am on the right track with this.
class SessionsController < Devise::SessionsController
def new
super
end
def create
post_params = {
"RuntimeEnvironment" => 1,
"Email" => params[:session][:email],
"Password" => params[:session][:password]
}.to_json
user_params = RestClient.post 'http://some.ip/WebServices', post_params, :content_type => "json"
user = User.authenticate(user_params)
if user
session[:user_id] = user.user_id
redirect_to root_path
else
flash.now.alert = "Invalid Username or Password"
render "new"
end
end
end
This is the JSON Object returned if there is a successful login:
{"Success":true,"ErrorMessage":"","ResponseString":"","LoginResultData":{"FailMessage":"","ResultCode":0,"User":{"AccountCompleteFlag":1,"CreationDtime":"\/Date(1430848539000-0400)\/","DeleteFlag":0,"Email":"john#doe.com","FailedPasswordCount":1,"HistoricalFlag":0,"IsDirty":false,"IsAdminFlag":0,"IsSiteAdminFlag":0,"LastLoginDtime":"\/Date(1447789258000-0500)\/","NameFirst":"Ttest","NameLast":"test","Password":"TRQt3d2Z7caDsSKL0ARVRd8nInks+pIyTSqp3BLxUgg=","PasswordLockDtime":"\/Date(-62135578800000-0500)\/","PasswordLockFlag":0,"PasswordResetCode":"","PasswordResetStatus":0,"Phone":"1-X-5555555555-","RegistrationSource":"Registration","UserId":100029,"UserType":1,"PhoneInfo":{"AreaCode":"555","CountryCode":"X","Extension":"","FirstThree":"555","InternationalPhoneNumber":"","IsDirty":false,"IsInternational":false,"LastFour":"5555"}}}}
And what is returned for a failed one:
{"Success":true,"ErrorMessage":"","ResponseString":"","LoginResultData":{"FailMessage":"Invalid email address","ResultCode":1,"User":null}}
Is there a way where I can use Devise's session management while connecting to the API?
You can still authenticate through Devise using the email and password that the user provided. The RestClient would just be like a double check: just make sure that there are no routes that the user can authenticate through besides going through the RestClient. You can check this by doing rake routes.
For checking whether the result code was valid, you can do some JSON parsing as follows:
authentication_response = RestClient.post 'http://some.ip/WebServices', post_params, :content_type => "json"
json_authentication_response = JSON.parse(authentication_response)
result_code = json_authentication_response["LoginResultData"]["ResultCode"]
if result_code == 0
# Authenticate
else
# Don't authenticate
end

Upgrade to rails 4.1.8 and my api broke

I haven't had the time to go through .6 and .7 as well as .8 in depth to make sure that I wasn't missing something, but today I decided to update my application, which is mostly an API with some front end work.
I updated the application to rails 4.1.8 and I got a lot of errors in my specs, in fact anything where the api had to be authenticated it blew up.
My controllers that use an api have a method called restrict_api_access which is done via
def restrict_api_access
authenticate_or_request_with_http_token do |token, options|
#api_key = ApiKey.find_by(api_key: token)
end unless current_user
end
Now you can log into the app and manage things, in that case we still use the same API that you would access say via a terminal or out side app. so the unless current_user basically looks like like:
def current_user
if (#api_key)
#current_user = User.find_by!(id: #api_key.xaaron_users_id)
else
super
end
end
Ans then the super of this current user basically states - are you logged in or not.
So one of the tests that are failing is set up like such:
context "Create new tag" do
before(:each) do
#tag = FactoryGirl.build(:tag, blog_id: #blog.id)
set_auth_header(#user)
end
it "should create a tag for a post" do
post :create, :blog_id => #blog.id, tag: {name: #tag.name, description: 'asdasddsa'}
json = JSON.parse(response.body)
expect(json['tag']['name']).to eql #tag.name
end
end
#user is set up else where. But I can assure you that a user is being passed in and is created.
The other interesting method here is set_auth_header(#user) this method looks like:
# Create a random api key
def create_api_key(user)
ApiKey.create!(:xaaron_users_id => user.id, :api_key => SecureRandom.hex(16))
end
# Set authentication headers for the API use
def set_auth_header(user)
key = create_api_key(user)
request.env['HTTP_AUTHORIZATION'] = "Token #{key.api_key}"
end
You can see we create an API key with the user, we then set the request environment variable to have that api key.
So before 4.1.8 (I was on 4.1.5) this worked fine, all my tests would pass life was grand. Now, with this particular test, I get:
1) Api::V1::TagsController Create new tag should create a tag for a post
Failure/Error: json = JSON.parse(response.body)
JSON::ParserError:
757: unexpected token at 'HTTP Token: Access denied.
'
# ./spec/controllers/api/v1/tags_controller_spec.rb:112:in `block (3 levels) in <top (required)>'
Essentially I am denied access. I have traced this through to the restrict_api_access to the line: authenticate_or_request_with_http_token which holds, as a value: HTTP Token: Access denied.
Is there something that changed in 4.1.8 that would be causing all my api that requires authentication to break?
You're computing your authorization header manually here, which might be wrong:
request.env['HTTP_AUTHORIZATION'] = "Token #{key.api_key}"
There's an ActionController method that does this for you:
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Token.encode_credentials(key.api_key)
Try that instead.

Rubymotion: GET Request using JSON

I am new to iOS programming and using Rubymotion to build my first application. In my Rubymotion app I POST to a webserver (an app built using RoR) to authenticate a user wanting to Login and am using the BubbleWrap gem for RubyMotion https://github.com/rubymotion/BubbleWrap/blob/master/motion/http.rb
def login(sender)
payload = {email: #email, password: #password}
BubbleWrap::HTTP.post("http://example.com/sessions", {payload: payload}) do |response|
#authCookie = response.headers['Set-Cookie']
end
end
Now once I receive successful authentication I move onto to receive JSON data from the web application using the following code:
BubbleWrap::HTTP.get("http://example.com/events.json", {cookie: #authCookie, :headers=>{"Content-Type"=>'json'} }) do |response|
puts response.body #testing the response
end
For some reason, the authentication token received from the POST request is not being correctly passed on by the GET request. I know this because in the web app if authentification fails it redirects to the login page and that's the response (HTML code of the Login page) am receiving from the GET request.
Authentication check on the Web App:
def session_check
if session[:bizid].nil?
redirect_to login_url
flash[:notice] = "Please login to view your account!"
end
end
Additionally, on the web app this authentication token is set by the following method:
def create
current_biz = Bizname.find_by_email(params[:email])
if current_biz && current_biz.authenticate(params[:password])
session[:bizid] = current_biz.id
flash[:notice] = 'Login Successful!'
if current_biz.events.empty?
redirect_to getsetup_url
else
redirect_to account_summary_url
end
else
flash[:notice] = 'Incorrect Email or Password.'
redirect_to login_url
end
end
Any ideas of what I might be doing wrong here?
Thanks in advance!
You just need to pass the cookie in as a header too. For example:
BubbleWrap::HTTP.get("http://example.com/events.json", :headers=> {"Content-Type"=>'json', "Cookie" => #authCookie}) do |response|
puts response.body #testing the response
end

Devise hijacking a 401 response

I'm trying to create an API and for one of my actions I'm restricting it to just admins. To do this, I'm using a before_filter that goes like this:
def authorize_admin!
if !#current_user.admin?
error = { :error => "You must be an admin to do that." }
render params[:format].to_sym => error, :status => 401
end
end
The problem is that when we send back a 401 response, the error is transformed into:
"{\"error\":\"You need to sign in or sign up before continuing.\"}"
This is the response that Devise sends back for when you send a 401 response.
Is there a way that I can turn off this functionality?
What is the Devise version? This wiki page suggests that it should work on 1.2.
https://github.com/plataformatec/devise/wiki/How-To:-Provide-a-custom-failure-response-with-Warden
I think that this page from the Devise wiki can help you.
How To Use HTTP Authentication in Devise

Resources