Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Custom Authentication Controllers
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]=dnlserrano@mail.com&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 'user_email=dnlserrano@mail.com&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]=dnlserrano@mail.com&user[password]=password' localhost:3000/login > ~/Desktop/new.html
```
### Greetings (needs to authenticate):
```
curl -X GET -H "Accept: application/json" --data 'user_email=dnlserrano@mail.com&user_token=<token>' localhost:3000/greetings > ~/Desktop/new.html
```
### Delete account:
```
curl -X DELETE -H "Accept: application/json" --data 'user_email=dnlserrano@mail.com&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 'user@test.com'
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] = "second_email@test.com"
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 => "invalid@email.com",
: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 => 'user@test.com',
: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
@waaadim

This comment has been minimized.

Copy link

@waaadim waaadim commented May 22, 2014

Hi I'm trying to implement your code here and I'm getting this error message:

 Failure/Error: post :create, req, format: :json
 AbstractController::ActionNotFound:
   Could not find devise mapping for path "/register?login%5Bemail%5D=user%40test.com&login%5Bfirst_name%5D=Daniel&login%5Blast_name%5D=Serrano&login%5Bpassword%5D=password&login%5Bpassword_confirmation%5D=password&login%5Busername%5D=test".
   This may happen for two reasons:

   1) You forgot to wrap your route inside the scope block. For example:

     devise_scope :user do
       get "/some/route" => "some_devise_controller"
     end

   2) You are testing a Devise controller bypassing the router.
      If so, you can explicitly tell Devise which mapping to use:

      @request.env["devise.mapping"] = Devise.mappings[:user]
@dnlserrano

This comment has been minimized.

Copy link
Owner Author

@dnlserrano dnlserrano commented Jul 19, 2014

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

@bdwain

This comment has been minimized.

Copy link

@bdwain bdwain commented Sep 21, 2014

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.

@dawidof

This comment has been minimized.

Copy link

@dawidof dawidof commented Oct 25, 2014

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!'
@dawidof

This comment has been minimized.

Copy link

@dawidof dawidof commented Oct 25, 2014

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)
@aihaddad

This comment has been minimized.

Copy link

@aihaddad aihaddad commented Nov 8, 2014

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment