Skip to content

Commit

Permalink
Facebook login.
Browse files Browse the repository at this point in the history
  • Loading branch information
hhorikawa committed May 14, 2020
1 parent 8e030b2 commit e6f263b
Show file tree
Hide file tree
Showing 21 changed files with 133 additions and 60 deletions.
12 changes: 11 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,26 @@

source 'https://rubygems.org'

# Downgrade (remove default flag):
# gem environment
# - INSTALLATION DIRECTORY: /usr/local/lib/ruby/gems/2.5.0
# rm /usr/local/lib/ruby/gems/2.5.0/specifications/default/bundler-2.1.4.gemspec
gem 'bundler', '~> 1.17'

# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 4.2.11'

# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
# 'Ruby Sass' has reached EOL and should no longer be used.
#gem 'sass-rails', '~> 5.0'

# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'

# Use jquery as the JavaScript library
gem 'jquery-rails'

# Squeel unlocks the power of Arel.
# Supporting Rails 3 and 4.
gem 'squeel'

Expand All @@ -27,6 +34,9 @@ gem 'validate_url'
gem 'validate_email'

# Facebook.
# 2020年5月現在, 比較的最近までメンテナンスされているものは、次の2択:
# - https://github.com/nov/fb_graph2
# - https://github.com/arsduo/koala
gem 'fb_graph2'

gem 'rack-oauth2'
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ To run this in development mode on your local machine:
* modify `config/connect/id_token/issuer.yml` -- change `issuer` to http://localhost:3000
* `bundle exec rails server -p 3000`

Facebook
config/connect/facebook.yml
Set `client_id` and `client_secret`

Point your browser at http://localhost:3000

If you download and run [the sample RP server](https://connect-rp.herokuapp.com),
Expand Down
4 changes: 4 additions & 0 deletions app/assets/config/manifest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

//= link_tree ../images
//= link_directory ../javascripts .js
//= link_directory ../stylesheets .css
File renamed without changes.
File renamed without changes.
2 changes: 2 additions & 0 deletions app/controllers/dashboard_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ class DashboardController < ApplicationController
before_filter :require_authentication

def show
@account = current_account
@clients = current_account.clients
print Connect::Facebook.all.inspect # DEBUG
end
end
6 changes: 3 additions & 3 deletions app/models/account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

# ユーザアカウント
class Account < ActiveRecord::Base
has_one :facebook, class_name: 'Connect::Facebook'
has_one :google, class_name: 'Connect::Google'
has_one :fake, class_name: 'Connect::Fake'
has_one :facebook, class_name: 'Connect::Facebook', dependent: :destroy
has_one :google, class_name: 'Connect::Google', dependent: :destroy
has_one :fake, class_name: 'Connect::Fake', dependent: :destroy

# 'developer' account が client を所有する.
# 通常, 開発者と一般ユーザで、アカウントをまったく分けたりはしない.
Expand Down
5 changes: 5 additions & 0 deletions app/models/connect/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

class Connect::Base < ActiveRecord::Base
self.abstract_class = true
belongs_to :account
end
23 changes: 17 additions & 6 deletions app/models/connect/facebook.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# -*- coding:utf-8 -*-

class Connect::Facebook < ActiveRecord::Base
belongs_to :account

class Connect::Facebook < Connect::Base
validates :identifier, presence: true, uniqueness: true
validates :access_token, presence: true, uniqueness: true

Expand Down Expand Up @@ -42,12 +41,24 @@ def auth
FbGraph2::Auth.new config[:client_id], config[:client_secret]
end

# Facebook client-side は Implicit Flow なので, 必ずトークンの検証が必要.
# これを怠ると, token hijacking される。
# Facebook サイトの文書は, 相互に異なったことが書いてあったり、
# そもそも検証が必要ということを強調しておらず、ひどい。
def authenticate(cookies)
# fb_graph2 では, from_cookie() 内で, SignedRequest の検証を自動的に行う.
# client_secret のハッシュ値との比較。
# => なので, client_secret が必要。
_auth_ = auth.from_cookie(cookies)
connect = find_or_initialize_by_identifier _auth_.user.identifier
print _auth_.inspect # DEBUG
connect = find_or_initialize_by(identifier: _auth_.user.identifier)
print connect.inspect # DEBUG
connect.access_token = _auth_.access_token.access_token
connect.save!
connect.account || Account.create!(facebook: connect)
Account.transaction do
connect.account || Account.create!(facebook: connect)
connect.save!
end
return connect.account
end
end
end
4 changes: 2 additions & 2 deletions app/models/connect/fake.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class Connect::Fake < ActiveRecord::Base
belongs_to :account

class Connect::Fake < Connect::Base

def userinfo
OpenIDConnect::ResponseObject::UserInfo.new(
Expand Down
12 changes: 7 additions & 5 deletions app/models/connect/google.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
# -*- coding:utf-8 -*-


class Connect::Google < ActiveRecord::Base
class Connect::Google < Connect::Base
serialize :id_token

belongs_to :account

validates :identifier, presence: true, uniqueness: true
validates :access_token, presence: true, uniqueness: true

Expand Down Expand Up @@ -83,6 +81,7 @@ def jwks
end

def authenticate(code)
# token の検証
client.authorization_code = code
token = client.access_token! :secret_in_body
id_token = OpenIDConnect::ResponseObject::IdToken.decode(
Expand All @@ -91,8 +90,11 @@ def authenticate(code)
connect = find_or_initialize_by(identifier: id_token.subject)
connect.access_token = token.access_token
connect.id_token = id_token
connect.save!
connect.account || Account.create!(google: connect)
Account.transaction do
connect.account || Account.create!(google: connect)
connect.save!
end
return connect.account
end
end # class << self
end
45 changes: 25 additions & 20 deletions app/views/connect/facebook/_login_button.html.erb
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
<fb:login-button
size="large"
onlogin="location.href = '<%= connect_facebook_path %>'"
scope="<%= Connect::Facebook.config[:scope]%>"
button_type="continue_with"
use_continue_as="true"
></fb:login-button>

<% content_for :top do %>
<div id="fb-root"></div>
<% end %>

<% content_for :bottom do %>
<script src="https://connect.facebook.net/en_US/all.js"></script>
<script>
<script>
window.fbAsyncInit = function() {
FB.init({
appId: "<%= Connect::Facebook.config[:client_id] %>",
cookie: true,
xfbml: true,
oauth: true,
version: 'v2.9'
appId: '<%= Connect::Facebook.config[:client_id] %>',
cookie: true,
xfbml: true,
version: 'v7.0'
});
</script>
<% end %>
};

(function(d, s, id) { // Load the SDK asynchronously
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "https://connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>
<% end %>

<fb:login-button size="large"
scope="<%= Connect::Facebook.config[:scope] %>"
button_type="continue_with"
use_continue_as="true"
onlogin="location.href = '<%= connect_facebook_path %>';">
</fb:login-button>

6 changes: 5 additions & 1 deletion app/views/dashboard/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<section>
<h2><%= 'dashboard.title'.t %></h2>

<p>Account: <%= @account.inspect %>
<p>Connect::Facebook: <%= @account.facebook.inspect %>

<%= render @clients %>
</section>

Expand All @@ -22,4 +26,4 @@
<dd><%= user_info_url %></dd>
</dl>
</details>
</aside>
</aside>
22 changes: 11 additions & 11 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
<!DOCTYPE html>
<html xmlns:fb="http://www.facebook.com/2008/fbml">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title><%= 'site.title'.t %> - <%= 'site.description'.t %></title>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title><%= 'site.title'.t %> - <%= 'site.description'.t %></title>

<link rel="stylesheet" href="/stylesheets/layout.css" />
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= stylesheet_link_tag params[:controller] %>
<%= javascript_include_tag 'application' %>
<link rel="stylesheet" href="/stylesheets/layout.css" />
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= stylesheet_link_tag params[:controller] %>
<%= javascript_include_tag 'application' %>

<%= csrf_meta_tags %>
<%= apple_app_capable_meta_tag %>
<%= apple_app_icon_link_tag %>
</head>
<%= content_tag :body, class: controller_name do %>
</head>
<%= content_tag :body, class: controller_name do %>
<%= yield :top %>
<%= render 'layouts/header' %>

Expand Down
3 changes: 3 additions & 0 deletions config/boot.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@

require 'rubygems'
gem 'bundler'

# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)

Expand Down
24 changes: 20 additions & 4 deletions config/connect/facebook.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@

# Apps:
# https://developers.facebook.com/apps/

# Permissions Reference - Facebook Login
# https://developers.facebook.com/docs/facebook-login/permissions

development: &defaults
client_id: 131909826902957
client_secret: 101c7ebebb894c551bbe9f461bfe9e8f
scope: user_birthday, user_location, user_website, email
client_id: XXXXXXXXXXXXX
client_secret: XXXXXXXXXXXXXXXXXXXXXXXXXXX
scope: user_birthday,user_location,email,user_friends,user_gender,user_hometown

# Requires the App Review:
# - user_age_range
# - user_birthday
# - user_friends
# - user_gender
# - user_hometown
# - user_location
# - user_photos

test:
<<: *defaults

production:
<<: *defaults
<<: *defaults
7 changes: 5 additions & 2 deletions db/migrate/20110829024031_create_connect_facebooks.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# -*- coding: utf-8 -*-

class CreateConnectFacebooks < ActiveRecord::Migration
def self.up
create_table :connect_facebook do |t|
t.belongs_to :account
t.string :identifier, :access_token
t.belongs_to :account, foreign_key: true
t.string :identifier, null: false, unique: true
t.string :access_token, unique: true
t.timestamps
end
end
Expand Down
5 changes: 3 additions & 2 deletions db/migrate/20110829024041_create_connect_googles.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
class CreateConnectGoogles < ActiveRecord::Migration
def self.up
create_table :connect_google do |t|
t.belongs_to :account
t.string :identifier, :access_token
t.belongs_to :account, foreign_key: true
t.string :identifier, null: false, unique: true
t.string :access_token, unique: true
t.text :id_token
t.timestamps
end
Expand Down
2 changes: 1 addition & 1 deletion db/migrate/20110910061828_create_connect_fakes.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class CreateConnectFakes < ActiveRecord::Migration
def self.up
create_table :connect_fakes do |t|
t.belongs_to :account
t.belongs_to :account, foreign_key: true
t.timestamps
end
end
Expand Down
4 changes: 2 additions & 2 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@

create_table "connect_facebook", force: :cascade do |t|
t.integer "account_id"
t.string "identifier"
t.string "identifier", null: false
t.string "access_token"
t.datetime "created_at"
t.datetime "updated_at"
Expand All @@ -109,7 +109,7 @@

create_table "connect_google", force: :cascade do |t|
t.integer "account_id"
t.string "identifier"
t.string "identifier", null: false
t.string "access_token"
t.text "id_token"
t.datetime "created_at"
Expand Down
3 changes: 3 additions & 0 deletions lib/authentication.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,11 @@ def required_scopes

def authenticate(account)
if account
raise TypeError if !account.is_a?(Account)
@current_account = account
session[:current_account] = account.id
else
unauthenticate!()
end
end

Expand Down

0 comments on commit e6f263b

Please sign in to comment.