From 230295790b057175606cca09c3093ba4bfde0a55 Mon Sep 17 00:00:00 2001 From: Graham Kaplan Date: Sat, 5 Apr 2014 19:18:57 -0700 Subject: [PATCH] Add Facebook login with Sorcery The facebook key and secret defined in the sorcery config file are for testing only and need to be changed and not committed to source when app is ready to go live --- app/assets/javascripts/oauths.js.coffee | 3 ++ app/assets/stylesheets/oauths.css.scss | 3 ++ app/controllers/oauths_controller.rb | 32 ++++++++++++++++++++++ app/helpers/oauths_helper.rb | 2 ++ app/models/authentication.rb | 3 ++ app/models/user.rb | 6 +++- app/views/oauths/callback.html.haml | 2 ++ app/views/oauths/oauth.html.haml | 2 ++ app/views/user_sessions/new.html.haml | 2 ++ config/initializers/sorcery.rb | 23 ++++++++-------- config/routes.rb | 4 +++ spec/spec_helper.rb | 3 ++ spec/support/authentication.rb | 8 ++++++ test/controllers/oauths_controller_test.rb | 14 ++++++++++ test/factories/authentications.rb | 6 ++++ test/helpers/oauths_helper_test.rb | 4 +++ test/models/authentication_test.rb | 7 +++++ 17 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 app/assets/javascripts/oauths.js.coffee create mode 100644 app/assets/stylesheets/oauths.css.scss create mode 100644 app/controllers/oauths_controller.rb create mode 100644 app/helpers/oauths_helper.rb create mode 100644 app/models/authentication.rb create mode 100644 app/views/oauths/callback.html.haml create mode 100644 app/views/oauths/oauth.html.haml create mode 100644 spec/support/authentication.rb create mode 100644 test/controllers/oauths_controller_test.rb create mode 100644 test/factories/authentications.rb create mode 100644 test/helpers/oauths_helper_test.rb create mode 100644 test/models/authentication_test.rb diff --git a/app/assets/javascripts/oauths.js.coffee b/app/assets/javascripts/oauths.js.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/app/assets/javascripts/oauths.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/oauths.css.scss b/app/assets/stylesheets/oauths.css.scss new file mode 100644 index 0000000..c879339 --- /dev/null +++ b/app/assets/stylesheets/oauths.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Oauths controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/oauths_controller.rb b/app/controllers/oauths_controller.rb new file mode 100644 index 0000000..bde25df --- /dev/null +++ b/app/controllers/oauths_controller.rb @@ -0,0 +1,32 @@ +class OauthsController < ApplicationController + skip_before_filter :require_login + + # sends the user on a trip to the provider, + # and after authorizing there back to the callback url. + def oauth + login_at(auth_params[:provider]) + end + + def callback + provider = auth_params[:provider] + if @user = login_from(provider) + redirect_to root_path, :notice => "Logged in with #{provider.titleize}!" + else + begin + @user = create_from(auth_params[:provider]) + + reset_session + auto_login(@user) + redirect_to root_path, :notice => "Signed up with #{provider.titleize}!" + rescue + redirect_to root_path, :alert => "Failed to login with #{provider.titleize}!" + end + end + end + + private + def auth_params + params.permit(:code, :provider) + end + +end \ No newline at end of file diff --git a/app/helpers/oauths_helper.rb b/app/helpers/oauths_helper.rb new file mode 100644 index 0000000..d6a2251 --- /dev/null +++ b/app/helpers/oauths_helper.rb @@ -0,0 +1,2 @@ +module OauthsHelper +end diff --git a/app/models/authentication.rb b/app/models/authentication.rb new file mode 100644 index 0000000..69a2df1 --- /dev/null +++ b/app/models/authentication.rb @@ -0,0 +1,3 @@ +class Authentication < ActiveRecord::Base + belongs_to :user +end diff --git a/app/models/user.rb b/app/models/user.rb index 9d312f0..ed8ff62 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,5 +1,7 @@ class User < ActiveRecord::Base - authenticates_with_sorcery! + authenticates_with_sorcery! do |config| + config.authentications_class = Authentication + end validates :password, presence: true, confirmation: true, length: { minimum: 3 }, unless: ("id?" || "password_confirmation?") validates :password_confirmation, presence: true, unless: ("id?" || "password?") @@ -16,5 +18,7 @@ class User < ActiveRecord::Base has_many :user_organization_relationships has_many :organizations, through: :user_organization_relationships + has_many :authentications, :dependent => :destroy + accepts_nested_attributes_for :authentications end diff --git a/app/views/oauths/callback.html.haml b/app/views/oauths/callback.html.haml new file mode 100644 index 0000000..425fb4c --- /dev/null +++ b/app/views/oauths/callback.html.haml @@ -0,0 +1,2 @@ +%h1 Oauths#callback +%p Find me in app/views/oauths/callback.html.haml diff --git a/app/views/oauths/oauth.html.haml b/app/views/oauths/oauth.html.haml new file mode 100644 index 0000000..a694b6c --- /dev/null +++ b/app/views/oauths/oauth.html.haml @@ -0,0 +1,2 @@ +%h1 Oauths#oauth +%p Find me in app/views/oauths/oauth.html.haml diff --git a/app/views/user_sessions/new.html.haml b/app/views/user_sessions/new.html.haml index 820fd0a..35cd257 100644 --- a/app/views/user_sessions/new.html.haml +++ b/app/views/user_sessions/new.html.haml @@ -8,4 +8,6 @@ Create an Account %p #{_ 'user.why_register', 'paragraph'} + = link_to 'Login with Facebook', auth_at_provider_path(:provider => :facebook) = render '/users/form' + diff --git a/config/initializers/sorcery.rb b/config/initializers/sorcery.rb index d32bd20..750459f 100644 --- a/config/initializers/sorcery.rb +++ b/config/initializers/sorcery.rb @@ -71,7 +71,7 @@ Rails.application.config.sorcery.configure do |config| # What providers are supported by this app, i.e. [:twitter, :facebook, :github, :linkedin, :xing, :google, :liveid] . # Default: `[]` # - # config.external_providers = + config.external_providers = [:facebook] # You can change it by your local ca_file. i.e. '/etc/pki/tls/certs/ca-bundle.crt' @@ -109,13 +109,14 @@ Rails.application.config.sorcery.configure do |config| # config.twitter.secret = "" # config.twitter.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=twitter" # config.twitter.user_info_mapping = {:email => "screen_name"} - # - # config.facebook.key = "" - # config.facebook.secret = "" - # config.facebook.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=facebook" - # config.facebook.user_info_mapping = {:email => "name"} - # config.facebook.access_permissions = ["email", "publish_stream"] - # + + config.facebook.key = "726202304080642" + config.facebook.secret = "386a7b717d348af4120aeb1bb0ca3516" + config.facebook.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=facebook" + config.facebook.user_info_mapping = {:email => "email", :username => "username", :avatar => "picture/data/url"} + config.facebook.scope = "email" + config.facebook.display = "popup" + # config.github.key = "" # config.github.secret = "" # config.github.callback_url = "http://0.0.0.0:3000/oauth/callback?provider=github" @@ -147,7 +148,7 @@ Rails.application.config.sorcery.configure do |config| # specify username attributes, for example: [:username, :email]. # Default: `[:username]` # - # user.username_attribute_names = + user.username_attribute_names = [:username, :email] # change *virtual* password attribute, the one which is used until an encrypted one is generated. @@ -214,7 +215,7 @@ Rails.application.config.sorcery.configure do |config| # make this configuration inheritable for subclasses. Useful for ActiveRecord's STI. # Default: `false` # - # user.subclasses_inherit_config = + user.subclasses_inherit_config = true # -- remember_me -- @@ -411,7 +412,7 @@ Rails.application.config.sorcery.configure do |config| # Class which holds the various external provider data for this user. # Default: `nil` # - # user.authentications_class = + user.authentications_class = Authentication # User's identifier in authentications class. diff --git a/config/routes.rb b/config/routes.rb index 2e373e4..5b9d4ea 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -56,6 +56,10 @@ BikeBike::Application.routes.draw do get 'login' => 'user_sessions#new', :as => :login post 'logout' => 'user_sessions#destroy', :as => :logout get 'register' => 'users#new', :as => 'register' + + post "oauth/callback" => "oauths#callback" + get "oauth/callback" => "oauths#callback" + get "oauth/:provider" => "oauths#oauth", :as => :auth_at_provider root 'pages#home' diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9e2f87e..3c4e392 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -23,6 +23,8 @@ require 'capybara/rspec' #Capybara.ignore_hidden_elements = false # testing hidden fields +include Sorcery::TestHelpers::Rails + RSpec.configure do |config| # == Mock Framework # @@ -33,6 +35,7 @@ RSpec.configure do |config| # config.mock_with :rr # config.mock_with :rspec + config.include AuthenticationForFeatureRequest, type: :feature # Run specs in random order to surface order dependencies. If you find an # order dependency and want to debug it, you can fix the order by providing diff --git a/spec/support/authentication.rb b/spec/support/authentication.rb new file mode 100644 index 0000000..deb7d9d --- /dev/null +++ b/spec/support/authentication.rb @@ -0,0 +1,8 @@ +module AuthenticationForFeatureRequest + def login user, password = 'login' + user.update_attribute :password, password + + page.driver.post sessions_url, {email: user.email, password: password} + visit root_url + end +end \ No newline at end of file diff --git a/test/controllers/oauths_controller_test.rb b/test/controllers/oauths_controller_test.rb new file mode 100644 index 0000000..884a527 --- /dev/null +++ b/test/controllers/oauths_controller_test.rb @@ -0,0 +1,14 @@ +require 'test_helper' + +class OauthsControllerTest < ActionController::TestCase + test "should get oauth" do + get :oauth + assert_response :success + end + + test "should get callback" do + get :callback + assert_response :success + end + +end diff --git a/test/factories/authentications.rb b/test/factories/authentications.rb new file mode 100644 index 0000000..38adeeb --- /dev/null +++ b/test/factories/authentications.rb @@ -0,0 +1,6 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :authentication do + end +end diff --git a/test/helpers/oauths_helper_test.rb b/test/helpers/oauths_helper_test.rb new file mode 100644 index 0000000..8783ff3 --- /dev/null +++ b/test/helpers/oauths_helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class OauthsHelperTest < ActionView::TestCase +end diff --git a/test/models/authentication_test.rb b/test/models/authentication_test.rb new file mode 100644 index 0000000..f01e61d --- /dev/null +++ b/test/models/authentication_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class AuthenticationTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end