Last active
February 16, 2016 19:41
-
-
Save jpmckinney/3d9122a4be194bade9dc to your computer and use it in GitHub Desktop.
See https://github.com/rishabhp/sso-rails-provider/issues/3. The repo also ran `rails g devise:install`, `rails g devise:views` and `rails g devise User`, which aren't shown in the diff to keep it small.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git b/Gemfile a/Gemfile | |
index 257a263..4896a34 100644 | |
--- b/Gemfile | |
+++ a/Gemfile | |
@@ -43,4 +43,12 @@ group :development do | |
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring | |
gem 'spring' | |
+ | |
+ gem 'pry' | |
end | |
+ | |
+# gems for omniauth and devise | |
+gem 'devise' | |
+gem 'omniauth' | |
+gem 'omniauth-twitter' | |
+gem 'omniauth-facebook' | |
diff --git b/app/controllers/auth_controller.rb a/app/controllers/auth_controller.rb | |
new file mode 100644 | |
index 0000000..cdd7176 | |
--- /dev/null | |
+++ a/app/controllers/auth_controller.rb | |
@@ -0,0 +1,81 @@ | |
+class AuthController < ApplicationController | |
+ # This is our new function that comes before Devise's one | |
+ before_filter :authenticate_user_from_token!, :except => [:access_token] | |
+ | |
+ before_filter :authenticate_user!, :except => [:access_token] | |
+ skip_before_filter :verify_authenticity_token, :only => [:access_token] | |
+ | |
+ def authorize | |
+ # Note: this method will be called when the user | |
+ # is logged into the provider | |
+ # | |
+ # So we're essentially granting him access to our | |
+ # system by generating certain tokens and then | |
+ # redirecting him back to the params[:redirect_uri] | |
+ # with a random code and the params[:state] | |
+ | |
+ | |
+ AccessGrant.prune! | |
+ create_hash = { | |
+ client: application, | |
+ state: params[:state] | |
+ } | |
+ access_grant = current_user.access_grants.create(create_hash) | |
+ redirect_to access_grant.redirect_uri_for(params[:redirect_uri]) | |
+ end | |
+ | |
+ # POST | |
+ def access_token | |
+ application = Client.authenticate(params[:client_id], params[:client_secret]) | |
+ | |
+ if application.nil? | |
+ render :json => {:error => "Could not find application"} | |
+ return | |
+ end | |
+ | |
+ access_grant = AccessGrant.authenticate(params[:code], application.id) | |
+ if access_grant.nil? | |
+ render :json => {:error => "Could not authenticate access code"} | |
+ return | |
+ end | |
+ | |
+ access_grant.start_expiry_period! | |
+ render :json => {:access_token => access_grant.access_token, :refresh_token => access_grant.refresh_token, :expires_in => Devise.timeout_in.to_i} | |
+ end | |
+ | |
+ def user | |
+ hash = { | |
+ provider: 'sso', | |
+ id: current_user.id.to_s, | |
+ info: { | |
+ email: current_user.email, | |
+ }, | |
+ extra: { | |
+ # first_name: current_user.first_name, | |
+ # last_name: current_user.last_name | |
+ first_name: '', | |
+ last_name: '' | |
+ } | |
+ } | |
+ | |
+ render :json => hash.to_json | |
+ end | |
+ | |
+ protected | |
+ | |
+ def application | |
+ @application ||= Client.find_by_app_id(params[:client_id]) | |
+ end | |
+ | |
+ private | |
+ | |
+ def authenticate_user_from_token! | |
+ if params[:oauth_token] | |
+ access_grant = AccessGrant.where(access_token: params[:oauth_token]).take | |
+ if access_grant.user | |
+ # Devise sign in | |
+ sign_in access_grant.user | |
+ end | |
+ end | |
+ end | |
+end | |
diff --git b/app/controllers/callbacks_controller.rb a/app/controllers/callbacks_controller.rb | |
new file mode 100644 | |
index 0000000..0bf5b60 | |
--- /dev/null | |
+++ a/app/controllers/callbacks_controller.rb | |
@@ -0,0 +1,8 @@ | |
+class CallbacksController < ApplicationController | |
+ | |
+ def facebook | |
+ @user = User.from_omniauth(request.env["omniauth.auth"]) | |
+ sign_in_and_redirect @user | |
+ end | |
+ | |
+end | |
diff --git b/app/models/access_grant.rb a/app/models/access_grant.rb | |
new file mode 100644 | |
index 0000000..a36f161 | |
--- /dev/null | |
+++ a/app/models/access_grant.rb | |
@@ -0,0 +1,34 @@ | |
+class AccessGrant < ActiveRecord::Base | |
+ belongs_to :user | |
+ belongs_to :client | |
+ | |
+ before_create :generate_tokens | |
+ | |
+ # Generate random tokens | |
+ def generate_tokens | |
+ self.code = SecureRandom.hex(16) | |
+ self.access_token = SecureRandom.hex(16) | |
+ self.refresh_token = SecureRandom.hex(16) | |
+ end | |
+ | |
+ def self.prune! | |
+ delete_all(["created_at < ?", 3.days.ago]) | |
+ end | |
+ | |
+ def redirect_uri_for(redirect_uri) | |
+ if redirect_uri =~ /\?/ | |
+ redirect_uri + "&code=#{code}&response_type=code&state=#{state}" | |
+ else | |
+ redirect_uri + "?code=#{code}&response_type=code&state=#{state}" | |
+ end | |
+ end | |
+ | |
+ def self.authenticate(code, application_id) | |
+ AccessGrant.where("code = ? AND client_id = ?", code, application_id).first | |
+ end | |
+ | |
+ # Note: This is currently configured through devise, and matches the AuthController access token life | |
+ def start_expiry_period! | |
+ self.update_attribute(:access_token_expires_at, Time.now + Devise.timeout_in) | |
+ end | |
+end | |
diff --git b/app/models/client.rb a/app/models/client.rb | |
new file mode 100644 | |
index 0000000..94a95db | |
--- /dev/null | |
+++ a/app/models/client.rb | |
@@ -0,0 +1,7 @@ | |
+class Client < ActiveRecord::Base | |
+ | |
+ # Check whether a Client exists by app_id and app_secret | |
+ def self.authenticate(app_id, app_secret) | |
+ where(["app_id = ? AND app_secret = ?", app_id, app_secret]).first | |
+ end | |
+end | |
diff --git b/app/models/user.rb a/app/models/user.rb | |
index c822027..cf96d77 100644 | |
--- b/app/models/user.rb | |
+++ a/app/models/user.rb | |
@@ -1,6 +1,18 @@ | |
class User < ActiveRecord::Base | |
+ has_many :access_grants, dependent: :delete_all | |
+ | |
# Include default devise modules. Others available are: | |
# :confirmable, :lockable, :timeoutable and :omniauthable | |
devise :database_authenticatable, :registerable, | |
- :recoverable, :rememberable, :trackable, :validatable | |
+ :recoverable, :rememberable, :trackable, :validatable, | |
+ :omniauthable, omniauth_providers: [:facebook] | |
+ | |
+ def self.from_omniauth(auth) | |
+ where(provider: auth.provider, uid: auth.uid).first_or_create do |user| | |
+ user.provider = auth.provider | |
+ user.uid = auth.uid | |
+ user.email = auth.info.email | |
+ user.password = Devise.friendly_token[0,20] | |
+ end | |
+ end | |
end | |
diff --git b/config/initializers/devise.rb a/config/initializers/devise.rb | |
index 4c07f75..f1a6149 100644 | |
--- b/config/initializers/devise.rb | |
+++ a/config/initializers/devise.rb | |
@@ -259,4 +259,7 @@ Devise.setup do |config| | |
# When using OmniAuth, Devise cannot automatically set OmniAuth path, | |
# so you need to do it manually. For the users scope, it would be: | |
# config.omniauth_path_prefix = '/my_engine/users/auth' | |
+ | |
+ config.omniauth :facebook, FB_APP_ID, FB_APP_SECRET, | |
+ scope: 'email', info_fields: 'name, email' | |
end | |
diff --git b/config/routes.rb a/config/routes.rb | |
index fc2791d..5741cc9 100644 | |
--- b/config/routes.rb | |
+++ a/config/routes.rb | |
@@ -1,5 +1,16 @@ | |
Rails.application.routes.draw do | |
- devise_for :users | |
+ # Easier to make GET request from the client | |
+ devise_for :users, sign_out_via: [:get, :delete], | |
+ controllers: { omniauth_callbacks: 'callbacks' } | |
+ | |
+ root 'home#index' | |
+ | |
+ # Provider stuff | |
+ match '/auth/sso/authorize' => 'auth#authorize', via: :all | |
+ match '/auth/sso/access_token' => 'auth#access_token', via: :all | |
+ match '/auth/sso/user' => 'auth#user', via: :all | |
+ match '/oauth/token' => 'auth#access_token', via: :all | |
+ | |
# The priority is based upon order of creation: first created -> highest priority. | |
# See how all your routes lay out with "rake routes". | |
diff --git b/db/migrate/20150826052258_add_columns_to_users.rb a/db/migrate/20150826052258_add_columns_to_users.rb | |
new file mode 100644 | |
index 0000000..dec83f6 | |
--- /dev/null | |
+++ a/db/migrate/20150826052258_add_columns_to_users.rb | |
@@ -0,0 +1,6 @@ | |
+class AddColumnsToUsers < ActiveRecord::Migration | |
+ def change | |
+ add_column :users, :provider, :string | |
+ add_column :users, :uid, :string | |
+ end | |
+end | |
diff --git b/db/migrate/20150826060015_create_access_grants.rb a/db/migrate/20150826060015_create_access_grants.rb | |
new file mode 100644 | |
index 0000000..384feec | |
--- /dev/null | |
+++ a/db/migrate/20150826060015_create_access_grants.rb | |
@@ -0,0 +1,15 @@ | |
+class CreateAccessGrants < ActiveRecord::Migration | |
+ def change | |
+ create_table :access_grants do |t| | |
+ t.string :code | |
+ t.string :access_token | |
+ t.string :refresh_token | |
+ t.datetime :access_token_expires_at | |
+ t.integer :user_id | |
+ t.integer :client_id | |
+ t.string :state | |
+ | |
+ t.timestamps | |
+ end | |
+ end | |
+end | |
diff --git b/db/migrate/20150826083958_create_client.rb a/db/migrate/20150826083958_create_client.rb | |
new file mode 100644 | |
index 0000000..4e40ebb | |
--- /dev/null | |
+++ a/db/migrate/20150826083958_create_client.rb | |
@@ -0,0 +1,11 @@ | |
+class CreateClient < ActiveRecord::Migration | |
+ def change | |
+ create_table :clients do |t| | |
+ t.string :name | |
+ t.string :app_id | |
+ t.string :app_secret | |
+ | |
+ t.timestamps | |
+ end | |
+ end | |
+end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment