-
-
Save dnlserrano/24c975d63e721a02b883 to your computer and use it in GitHub Desktop.
class ApiController < ApplicationController | |
# define which model will act as token authenticatable | |
acts_as_token_authentication_handler_for Login | |
# Prevent CSRF attacks by raising an exception. | |
# For APIs, you may want to use :null_session instead. | |
protect_from_forgery with: :null_session | |
respond_to :json | |
skip_before_filter :verify_authenticity_token, if: :json_request? | |
private | |
def json_request? | |
request.format.json? | |
end | |
end |
MyApp::Application.routes.draw do | |
root :to => 'welcome#index' | |
devise_for :logins, :skip => [:sessions, :registrations, :passwords] | |
devise_scope :login do | |
post 'login' => 'sessions#create', :as => :login | |
delete 'logout' => 'sessions#destroy', :as => :logout | |
post 'register' => 'registrations#create', :as => :registers | |
delete 'delete_account' => 'registrations#destroy', :as => :delete_account | |
end | |
get 'greetings', to: 'greetings#index' | |
end |
### Signup (on signup, the user is automatically logged in): | |
``` | |
curl -X POST -H "Accept: application/json" --data 'user[username]=dnlserrano&user[email][email protected]&user[password]=password&user[password_confirmation]=password' localhost:3000/register > ~/Desktop/new.html | |
``` | |
### Logout (authenticates using token): | |
``` | |
curl -X DELETE -H "Accept: application/json" --data '[email protected]&user_token=<token>' localhost:3000/logout > ~/Desktop/new.html | |
``` | |
### Login (initiates session with valid email/password combination): | |
``` | |
curl -X POST -H "Accept: application/json" --data 'user[email][email protected]&user[password]=password' localhost:3000/login > ~/Desktop/new.html | |
``` | |
### Greetings (needs to authenticate): | |
``` | |
curl -X GET -H "Accept: application/json" --data '[email protected]&user_token=<token>' localhost:3000/greetings > ~/Desktop/new.html | |
``` | |
### Delete account: | |
``` | |
curl -X DELETE -H "Accept: application/json" --data '[email protected]&user_token=<token>' localhost:3000/delete_account > ~/Desktop/new.html | |
``` | |
class GreetingsController < ApiController | |
def index | |
end | |
end |
class CustomFailure < Devise::FailureApp | |
def redirect_url | |
root_path | |
end | |
def respond | |
if http_auth? | |
http_auth | |
else | |
if request.format.json? | |
self.status = :unauthorized | |
self.response_body = { :sucess => false, :data => { :message => "login failed" }}.to_json | |
self.content_type = "json" | |
else | |
redirect | |
end | |
end | |
end | |
end |
class Login < ActiveRecord::Base | |
# token authenticatable using simple_token_authentication gem | |
acts_as_token_authenticatable | |
# Include default devise modules. Others available are: | |
# :confirmable, :lockable, :timeoutable and :omniauthable | |
devise :database_authenticatable, :registerable, | |
:recoverable, :rememberable, :trackable, :validatable | |
has_one :user, dependent: :destroy | |
validates :email, :presence => true, :uniqueness => true, :email => true | |
end |
class User < ActiveRecord::Base | |
validates_uniqueness_of :username | |
validates_presence_of :username | |
validates_length_of :username, :minimum => 1 , :maximum => 20, :allow_nil => false | |
validates_presence_of :first_name | |
validates_length_of :last_name, :maximum => 60, :allow_nil => false | |
validates_presence_of :last_name | |
validates_length_of :last_name, :maximum => 60, :allow_nil => false | |
belongs_to :login | |
end |
class RegistrationsController < Devise::RegistrationsController | |
respond_to :json | |
skip_before_filter :verify_authenticity_token, if: :json_request? | |
acts_as_token_authentication_handler_for Login | |
skip_before_filter :authenticate_entity_from_token!, only: [ :create ] | |
skip_before_filter :authenticate_entity!, only: [ :create ] | |
skip_before_filter :authenticate_scope! | |
append_before_filter :authenticate_scope!, only: [ :destroy ] | |
def create | |
ActiveRecord::Base.transaction do | |
begin | |
build_resource(login_params) | |
if resource.save | |
resource.create_user!(user_params) | |
@success = true | |
else | |
clean_up_passwords resource | |
@success = false | |
@errors = resource.errors | |
raise t('.invalid_signup') | |
end | |
rescue Exception => e | |
@success = false | |
@errors = e.message if @errors.blank? and not e.message.nil? | |
raise ActiveRecord::Rollback | |
end | |
end | |
end | |
def destroy | |
resource.destroy | |
Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name) | |
end | |
private | |
def login_params | |
permitted_params.extract!(:email, :password, :password_confirmation).permit! | |
end | |
def user_params | |
permitted_params.extract!(:username, :first_name, :last_name, :bday).permit! | |
end | |
def permitted_params | |
params.require(:login).permit(:email, :password, :password_confirmation, :username, :first_name, :last_name, :bday) | |
end | |
def json_request? | |
request.format.json? | |
end | |
end |
class SessionsController < Devise::SessionsController | |
respond_to :json | |
skip_before_filter :verify_authenticity_token, if: :json_request? | |
acts_as_token_authentication_handler_for Login | |
skip_before_filter :authenticate_entity_from_token! | |
skip_before_filter :authenticate_entity! | |
before_filter :authenticate_entity_from_token!, :only => [:destroy] | |
before_filter :authenticate_entity!, :only => [:destroy] | |
def create | |
warden.authenticate!(:scope => resource_name) | |
@login = current_login | |
end | |
def destroy | |
if login_signed_in? | |
@login = current_login | |
@login.authentication_token = nil | |
@login.save | |
else | |
render 'failure' | |
end | |
end | |
private | |
def json_request? | |
request.format.json? | |
end | |
end |
FactoryGirl.define do | |
factory :login do | |
email '[email protected]' | |
password 'password' | |
end | |
end |
FactoryGirl.define do | |
factory :user do | |
login | |
username 'username' | |
first_name 'First' | |
last_name 'Last' | |
end | |
end |
require 'spec_helper' | |
require 'json' | |
RSpec.configure do |config| | |
config.include Devise::TestHelpers, :type => :controller | |
config.include RegistrationHelpers, :type => :controller | |
end | |
describe RegistrationsController do | |
before(:each) do | |
@request.env["devise.mapping"] = Devise.mappings[:login] | |
@request.env["HTTP_ACCEPT"] = "application/json" | |
@request.env["CONTENT_TYPE"] = "application/json" | |
end | |
it "creates an account" do | |
req = dummy_registration | |
post "create", req | |
content = req[:login] | |
login = Login.first | |
expect(login).not_to be_nil | |
expect(login.email).to eq(content[:email]) | |
expect(login.password).to eq(login.password_confirmation) | |
expect(login.authentication_token).not_to be_nil | |
expect(login.created_at).to be < Time.now | |
user = login.user | |
expect(user).not_to be_nil | |
expect(user.username).to eq(content[:username]) | |
expect(user.first_name).to eq(content[:first_name]) | |
expect(user.last_name).to eq(content[:last_name]) | |
expect(user.bday).to be_nil | |
expect(user.created_at).to be < Time.now | |
end | |
it "does not create a duplicate account" do | |
post "create", dummy_registration | |
post "create", dummy_registration | |
dummy_email = dummy_registration[:login][:email] | |
users = User.all | |
expect(users.count).to eq(1) | |
logins = Login.all | |
expect(logins.count).to eq(1) | |
user = users.first | |
login = logins.first | |
expect(user.login).to eq(login) | |
end | |
it "validates uniqueness of email" do | |
first_req = dummy_registration | |
second_req = dummy_registration | |
second_req[:login][:username] = "second_req_name" | |
post "create", first_req | |
post "create", second_req | |
users = User.all | |
expect(users.count).to eq(1) | |
logins = Login.all | |
expect(logins.count).to eq(1) | |
user = users.first | |
login = logins.first | |
expect(user.login).to eq(login) | |
end | |
it "validates uniqueness of username" do | |
first_req = dummy_registration | |
second_req = dummy_registration | |
second_req[:login][:email] = "[email protected]" | |
post "create", first_req | |
post "create", second_req | |
users = User.all | |
expect(users.count).to eq(1) | |
logins = Login.all | |
expect(logins.count).to eq(1) | |
user = users.first | |
login = logins.first | |
expect(user.login).to eq(login) | |
end | |
it "validates email format" do | |
wrong_req = dummy_registration | |
wrong_req[:login][:email] = "wrong_email" | |
post "create", wrong_req | |
wrong_req = dummy_registration | |
wrong_req[:login][:email] = "wrong_email@" | |
post "create", wrong_req | |
wrong_req = dummy_registration | |
wrong_req[:login][:email] = "wrong_email@a." | |
post "create", wrong_req | |
logins = Login.all | |
expect(logins.count).to be_zero | |
users = User.all | |
expect(users.count).to be_zero | |
end | |
it "validates presence of email" do | |
wrong_req = dummy_registration | |
wrong_req[:login][:email] = nil | |
post "create", wrong_req | |
logins = Login.all | |
expect(logins.count).to be_zero | |
users = User.all | |
expect(users.count).to be_zero | |
end | |
it "validates presence of first name" do | |
wrong_req = dummy_registration | |
wrong_req[:login][:first_name] = nil | |
post "create", wrong_req | |
logins = Login.all | |
expect(logins.count).to be_zero | |
users = User.all | |
expect(users.count).to be_zero | |
end | |
it "validates presence of last name" do | |
wrong_req = dummy_registration | |
wrong_req[:login][:last_name] = nil | |
post "create", wrong_req | |
logins = Login.all | |
expect(logins.count).to be_zero | |
users = User.all | |
expect(users.count).to be_zero | |
end | |
it "fails to destroy account for wrong user credentials" do | |
dummy_user = create(:user) | |
login = dummy_user.login | |
params = { | |
:login_email => "[email protected]", | |
:login_token => "1NV4LidT0k3N" | |
} | |
delete "destroy", params | |
logins = Login.all | |
expect(logins.count).to eq(1) | |
users = User.all | |
expect(users.count).to eq(1) | |
end | |
it "destroys existent account for correct user credentials" do | |
dummy_user = create(:user) | |
login = dummy_user.login | |
login_email = login.email | |
login_token = login.authentication_token | |
params = { | |
:login_email => login_email, | |
:login_token => login_token | |
} | |
delete "destroy", params | |
logins = Login.all | |
expect(logins.count).to be_zero | |
users = User.all | |
expect(users.count).to be_zero | |
end | |
end |
module RegistrationHelpers | |
def dummy_registration | |
dummy = { | |
:login => { | |
:email => '[email protected]', | |
:password => 'password', | |
:password_confirmation => 'password', | |
:username => 'test', | |
:first_name => 'Daniel', | |
:last_name => 'Serrano' | |
} | |
} | |
return dummy | |
end | |
end |
json.success true | |
json.data do | |
json.greeting 'Hello! You were able to authenticate, congrats!' | |
end |
json.success @success | |
json.data do | |
if @success | |
json.message t('.message') | |
else | |
json.errors @errors if not @errors.blank? | |
end | |
end |
json.success true | |
json.data do | |
json.message t('.message') | |
end |
json.success true | |
json.data do | |
json.auth_token @login.authentication_token | |
json.message t('.message') | |
end |
json.success true | |
json.data do | |
json.message t('.message') | |
end |
json.sucess false | |
json.data do | |
json.message t('.message') | |
end |
Hi there @waaadim,
I'm sorry for the late reply and I hope you have figured it out by now, but I only saw your comment today, as Github does not trigger notifications on gists (you can give this issue a thumbs up to encourage Github to solve the problem!).
As to your problem, it seems like your routes are missing the register action. Did you follow my routes.rb
file?
Best regards
Hi,
I'm curious why you have this line in routes.rb
devise_for :logins, :skip => [:sessions, :registrations, :passwords]
it seems like it generates no new routes.
Thank you for useful gist!
But Logout doesn't work for me
The error occurs
Completed 500 Internal Server Error in 2ms
ArgumentError (wrong number of arguments (0 for 1)):
simple_token_authentication (1.5.0) lib/simple_token_authentication/acts_as_token_authentication_handler.rb:28:in `authenticate_entity_from_token!'
Updated gems. Devise - 3.4.0, simple_token_authentication - 1.6.0
But now I receive another error:
Filter chain halted as :verify_signed_out_user rendered or redirected
Completed 204 No Content in 10ms (ActiveRecord: 0.0ms)
hi, @dnlserrano thanks for he gist and all the discussions going on. I am in a similar situation with this, except that mu user has a polymorphic belongs_to association with 4 roles. I've already created the user model to work with devise, and I was wondering if this example could be done without the Login model. This is my first API and I'd really appreciate your help.
Hi I'm trying to implement your code here and I'm getting this error message: