I want to do authenticate API using grape. For auth I used Devise gem. I try include devise::sessioncontroller into my grape api file but it's caput.
class SignIn < BaseAPI
resource :sign_in do
desc 'Sign in page'
params do
requires :username, type: String
end
post do
User.authenticate(params)
end
end
end
Try the following code. You should be able to authenticate. There are a few extra things you need to set up. Follow this document for more details.
resource :sign_in do
desc "Authenticate user"
params do
requires :login, type: String
requires :password, type: String
end
post :login do
user = User.find_by_email(params[:login].downcase)
if user && user.authenticate(params[:password])
token = TokenGenerator.create(user_id: user.id)
{token: token.access_token}
else
error!('Unauthorized.', 401)
end
end
end
Related
I'm using RSpec to test my implementation of the mixture of Devise and the simple_token_authentication gem.
I using devise_for to tell Devise where to look for my sessions controller (source).
devise_for :users, controllers: {
registrations: 'users/devise/registrations',
sessions: 'users/devise/sessions'
}
In my sessions controller, I invoke a custom method if the request has a specific header (source).
def create
req_from_coposition_app? ? respond_with_auth_token : super
end
def destroy
req_from_coposition_app? ? destroy_auth_token : super
end
I have a test that ensures the user can get an auth token.
The request works as expected (source).
it "should be able to sign in" do
request.headers["X-Secret-App-Key"] = "this-is-a-mobile-app"
request.env['devise.mapping'] = Devise.mappings[:user]
post :create,
user: {
email: user.email,
password: user.password
},
format: :json
expect(res_hash[:email]).to eq user.email
expect(res_hash[:authentication_token]).to eq user.authentication_token
end
Next, I'd like to test signing out (destroying the auth key).
I am trying to use the following code:
it "should be able to sign out" do
token_before = user.authentication_token
request.env['devise.mapping'] = Devise.mappings[:user]
request.headers["X-Secret-App-Key"] = "this-is-a-mobile-app"
request.headers["X-User-Token"] = token_before
delete :destroy, nil, format: :json
expect(user.reload.authentication_token).to_not eq token_before
end
However, the Users::Devise::SessionsController#destroy is never hit.
response.status is 302, and
response.body
#=> "<html><body>You are being redirected.</body></html>"
I have checked rake routes, formatted the request in different ways, and checked that the delete method wasn't being messed with anywhere. I am completely at loss as to why post :create works, but delete :destroy doesn't hit the action.
You're not fooling the before filter on line 4 here https://github.com/plataformatec/devise/blob/master/app/controllers/devise/sessions_controller.rb
Devise thinks no one is signed in and is bouncing you away from the destroy action. Skip it, stub it, trick it.
I have a rails application that contains all front-end part , administrative and register/login/logout (Devise).
I also have a part with more dynamic maps that is written in javascript React. It runs on a controller / view separately in the same application.
I created an api using Grape to expose the data to React.
My question is how to know that the user is logged in without the use of tokens.
Which way ? I can use cookies and session stored in the browser? How would?
I can get the user id by:
user_id = env['rack.session']['warden.user.user.key'].first.first
That would be fine?
User.find(user_id)
It's safe?
One of my application I have use devise authentication like below:
api.rb
#require 'grape'
module Base
class API < Grape::API
prefix 'api'
version 'v1', :using => :header, :vendor => 'vendor'
format :json
helpers do
def current_user
user = User.where(authentication_token: params[:auth_token], is_approved: true).first
if user
#current_user = user
else
false
end
end
def authenticate!
error!('401 Unauthorized', 401) unless current_user
end
end
# load the rest of the API
mount V1::Registration
mount V1::Sessions
end
end
sessions.rb
module V1
class Sessions < Grape::API
version 'v1', using: :path
format :json
prefix :api
resource :sessions do
##<$ User Sign In API $>##
desc 'Authenticate user and return user object / access token'
params do
requires :email, type: String, desc: 'User email'
requires :password, type: String, desc: 'User Password'
end
post do
email = params[:email]
password = params[:password]
if email.nil? or password.nil?
error!({error_code: 404, error_message: 'Invalid Email or Password.'}, 401)
return
end
user = User.where(email: email.downcase).first
if user.nil?
error!({error_code: 404, error_message: 'Invalid Email or Password.'}, 401)
return
end
if !user.valid_password?(password)
error!({error_code: 404, error_message: 'Invalid Email or Password.'}, 401)
return
else
user.ensure_authentication_token
user.save
{status: 'ok', auth_token: user.authentication_token}
end
end
desc 'Destroy the access token'
params do
requires :auth_token, type: String, desc: 'User Access Token'
end
delete ':auth_token' do
auth_token = params[:auth_token]
user = User.where(authentication_token: auth_token).first
if user.nil?
error!({error_code: 404, error_message: 'Invalid access token.'}, 401)
return
else
user.reset_authentication_token
{status: 'ok'}
end
end
end
end
end
After following the Hartl Tutorial I'm trying to change the authentication to use the devise gem. My sample application site seems to be working again but some of the specs still fail because some of the routes and user controller actions have changed. So I'm in the process of fixing those and stuck on one that checks to make sure the user can't give themselves admin access.
describe "update user with forbidden attributes", type: request do
FactoryGirl.create(:user)
let(:params) do
{ "user[name]" => "new name",
"user[email]" => user.email,
"user[current_password]" => user.password,
"admin" => true }
end
before do
post user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
patch user_registration_path(user), params
user.reload
end
its(:name) { should eql "new name" } # passes, and should.
its(:admin?) { should be false } # can't get to fail.
specify { expect(response).to be_success } # fails, gets response 406.
end
This test passes, but it passes because I can't get it to fail. I'm trying to do the usual Red-Green-Refactor and I can't make it go red, even if I add admin to the list of devise acceptable parameters. I want to make sure that this would change admin if the permissions were screwed up.
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: devise_controller?
after_action :print_permitted_parameters, if: devise_controller?
def configure_permitted_parameters
...
devise_parameter_sanitizer.for(:account_update) do |u|
u.permit(:name, :email, :password, :password_confirmation, :current_password, :admin)
end
end
def print_configured_parameters
puts "sign_up: " + devise_parameter_sanitizer.for(:sign_up).join(' ')
#prints "sign_up: email password password_confirmation"
puts "sign_in: " + devise_parameter_sanitizer.for(:sign_in).join(' ')
#prints "sign_in: email password remember_me"
puts "account_update: " + devise_parameter_sanitizer.for(:account_update).join(' ')
#prints "account_update: email password password_confirmation current_password"
end
end
The strange thing is that user's name and email do update, so something is working. But the response I get is always 406 for "Not Acceptable". So my question is why can I not get the admin tests to fail? And are the 406 errors related?
printing the permitted parameters suggests the parameters aren't being configured for any actions, it's just the default list. And I can sign_in with an existing user but if I just click "sign_in" with no fields it complains of an umpermitted parameter: "remember_me" despite that being on the list. Similarly if I try to sign_up a new user, which used to work, it complains that password_confirmation is unpermitted.
Thanks for your help, I appreciate it.
I want to use seperate admin login for my application using idenity provider.
I have written this in config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :identity, :model => Credential, :on_failed_registration =>SessionsController.action(:register)
provider :identity, :model => Credential, :name => 'admin', :on_failed_registration => SessionsController.action(:login_admin)
provider :google_oauth2, '000000000.apps.googleusercontent.com', '00000000000'
end
In config/routes.rb
match '/auth/admin/callback', :to => 'sessions#authenticate_admin'
In app/controllers/sessions_controller.rb
def authenticate_admin
auth_hash = request.env['omniauth.auth']
session[:admin_user] = auth_hash['user_info']['email']
if admin?
redirect_to '/'
else
render :text => '401 Unauthorized', :status => 401
end
end
But when i try to access request.env['omniauth.auth'], it always gets nil. While it is accessible when using default callback for normal users at sessison#create action. I just want to know if there is anything that has been missed in this code. I am following this blog http://www.intridea.com/blog/2011/1/31/easy-rails-admin-login-with-google-apps-and-omniauth.
I'm using rack/test and rspec on Rails 3 to authenticate a users api key through devise. Any request I make returns a status of 302 and a response of: "You are being redirected.". Can't seem to figure out how to authenticate.
require 'spec_helper'
describe Api::V1::CollectController do
def app
Rails.application
end
context 'user' do
subject do
FactoryGirl.create :user
end
it 'allow valid api credentials' do
post '/api/collect', {}, { 'Authentication' => subject.authentication_token }
p last_response.body
end
end
end
I've found a working solution. But I'm not satisfied with why the original approach didn't work. I eliminated rack/test and changed the way I interface with the controller. The following worked. I'm mostly just curious now...
require 'spec_helper'
describe Api::V1::CollectController do
context 'user' do
subject do
FactoryGirl.create :user
end
it 'allow valid api credentials' do
post :create, {}, { 'Authentication' => subject.authentication_token }
p response.body
end
end
end