Skip to content

Commit

Permalink
Add login with facebook
Browse files Browse the repository at this point in the history
  • Loading branch information
MaicolBen committed Oct 31, 2016
1 parent 194bd85 commit 2628c1d
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 4 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ gem 'haml', '~> 4.0.6'
gem 'inherited_resources', github: 'activeadmin/inherited_resources'
gem 'jbuilder', '~> 1.5.3'
gem 'oj', '~> 2.17.5'
gem 'koala', '~> 1.10.1'
gem 'puma', '~> 3.0'
gem 'rack-cors', '~> 0.4.0'
gem 'pg', '~> 0.18.2'
Expand Down
8 changes: 8 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ GEM
railties (>= 3.0.0)
faker (1.4.3)
i18n (~> 0.5)
faraday (0.9.2)
multipart-post (>= 1.2, < 3)
ffi (1.9.14)
fog-aws (0.12.0)
fog-core (~> 1.38)
Expand Down Expand Up @@ -191,6 +193,10 @@ GEM
kaminari (0.17.0)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
koala (1.10.1)
addressable
faraday
multi_json
launchy (2.4.3)
addressable (~> 2.3)
letter_opener (1.4.1)
Expand All @@ -212,6 +218,7 @@ GEM
mini_portile2 (2.1.0)
minitest (5.9.1)
multi_json (1.12.1)
multipart-post (2.0.0)
nio4r (1.2.1)
nokogiri (1.6.8.1)
mini_portile2 (~> 2.1.0)
Expand Down Expand Up @@ -384,6 +391,7 @@ DEPENDENCIES
haml (~> 4.0.6)
inherited_resources!
jbuilder (~> 1.5.3)
koala (~> 1.10.1)
letter_opener (~> 1.4.1)
listen (~> 3.0.5)
oj (~> 2.17.5)
Expand Down
20 changes: 20 additions & 0 deletions app/controllers/api/v1/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,29 @@ module V1
class SessionsController < DeviseTokenAuth::SessionsController
protect_from_forgery with: :null_session

def facebook
user_params = FacebookService.new(params[:access_token]).profile
@resource = User.from_social_provider 'facebook', user_params
custom_sign_in
rescue Koala::Facebook::AuthenticationError
render json: { error: 'Not Authorized' }, status: :forbidden
rescue ActiveRecord::RecordNotUnique
render json: { error: 'User already registered with email/password' }, status: :bad_request
end

def resource_params
params.require(:user).permit(:email, :password)
end

private

def custom_sign_in
sign_in(:api_v1_user, @resource)
new_auth_header = @resource.create_new_auth_token
# update response with the header that will be required by the next request
response.headers.merge!(new_auth_header)
render_create_success
end
end
end
end
7 changes: 7 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,11 @@ def full_name
return username unless first_name.present?
"#{first_name} #{last_name}"
end

def self.from_social_provider(provider, user_params)
where(provider: provider, uid: user_params['id']).first_or_create do |user|
user.password = Devise.friendly_token[0, 20]
user.assign_attributes user_params.except('id')
end
end
end
8 changes: 6 additions & 2 deletions app/services/facebook_service.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
class FacebookService < SocialNetworkService
class FacebookService
def initialize(access_token)
@access_token = access_token
end

def profile
client.get_object('me?fields=email,first_name,last_name')
end

def client
Koala::Facebook::API.new(@oauth_token, ENV['FACEBOOK_SECRET'])
Koala::Facebook::API.new(@access_token, ENV['FACEBOOK_SECRET'])
end
end
Empty file.
10 changes: 8 additions & 2 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@

namespace :api do
namespace :v1, defaults: { format: :json } do
get :status, to: 'api#status'
resources :users, only: [:show, :update]
devise_scope :user do
get :status, to: 'api#status'
resources :users, only: [:show, :update] do
controller :sessions do
post :facebook, on: :collection
end
end
end
end
end
end
80 changes: 80 additions & 0 deletions spec/controllers/api/v1/sessions_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,86 @@
end
end

describe 'POST facebook' do
shared_context 'fail to login with facebook' do
it 'does not returns a successful response' do
post :facebook, params: params
expect(response).to_not have_http_status(:success)
end

it 'does not create an user' do
expect { post :facebook, params: params }.to change(User, :count).by(0)
end
end
context 'with valid params' do
let(:params) do
{
access_token: '123456',
format: 'json'
}
end

it 'returns a successful response' do
post :facebook, params: params
expect(response).to have_http_status(:success)
end

it 'creates an user' do
expect { post :facebook, params: params }.to change(User, :count).by(1)
end

it 'assigns the information properly' do
post :facebook, params: params
user = User.last
expect(user.first_name).to eq 'Test'
expect(user.email).to eq 'test@facebook.com'
expect(user.uid).to eq '1234567890'
expect(user.provider).to eq 'facebook'
expect(user.encrypted_password).to be_present
end

it 'returns a valid client and access token' do
post :facebook, params: params
token = response.header['access-token']
client = response.header['client']
user = User.last
expect(user.reload.valid_token?(token, client)).to be_truthy
end

context 'with an user having the same email' do
before do
FactoryGirl.create :user, email: 'test@facebook.com'
end

it_behaves_like 'fail to login with facebook'
end

context 'without facebook email' do
let(:params) do
{
access_token: 'without_email',
format: 'json'
}
end

it 'creates an user' do
expect { post :facebook, params: params }.to change(User, :count).by(1)
end
end
end

context 'with invalid params' do
let(:params) do
{
access_token: 'invalid',
format: 'json'
}
end

it_behaves_like 'fail to login with facebook'
end
end

describe 'DELETE destroy' do
before do
auth_request(user)
Expand Down
18 changes: 18 additions & 0 deletions spec/support/mocks/facebook_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class FacebookService
def initialize(access_token)
@access_token = access_token
end

def profile
if @access_token.present? && @access_token != 'invalid'
{
'first_name' => 'Test',
'last_name' => 'test',
'email' => @access_token == 'without_email' ? '' : 'test@facebook.com',
'id' => '1234567890'
}
else
fail Koala::Facebook::AuthenticationError.new 400, 'error'
end
end
end

0 comments on commit 2628c1d

Please sign in to comment.