From 2a44a0f7f885e25ffa5e7349be8239ebc37e9b3a Mon Sep 17 00:00:00 2001 From: yasushiito Date: Thu, 16 Jul 2015 18:09:32 +0900 Subject: [PATCH] add: omniauth --- Gemfile | 1 + app/assets/images/google.png | Bin 0 -> 2684 bytes app/assets/images/twitter.png | Bin 0 -> 2830 bytes app/assets/javascripts/admin.js | 1 + app/assets/javascripts/omniauth_callbacks.coffee | 3 - app/assets/stylesheets/omniauth_callbacks.css.scss | 3 - app/assets/stylesheets/test.css.scss | 20 +++++++ app/controllers/omniauth_callbacks_controller.rb | 6 +- app/models/user.rb | 3 +- app/views/user_sessions/new.html.erb | 64 +++++++++++++-------- config/initializers/devise.rb | 1 + config/locales/pettanr.ja.yml | 11 +++- config/secrets.yml.org | 13 +++++ public/images/google.png | Bin 0 -> 2684 bytes public/images/twitter.png | Bin 0 -> 2830 bytes 15 files changed, 93 insertions(+), 33 deletions(-) create mode 100644 app/assets/images/google.png create mode 100644 app/assets/images/twitter.png delete mode 100644 app/assets/javascripts/omniauth_callbacks.coffee delete mode 100644 app/assets/stylesheets/omniauth_callbacks.css.scss create mode 100644 config/secrets.yml.org create mode 100644 public/images/google.png create mode 100644 public/images/twitter.png diff --git a/Gemfile b/Gemfile index 33046935..cb1e1fe8 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,7 @@ gem 'rails', '~> 4.1.0' gem 'devise' gem 'omniauth' gem 'omniauth-twitter' +gem 'omniauth-google-oauth2' gem 'rest-client' gem 'underscore-rails' # gem 'backbone-rails' diff --git a/app/assets/images/google.png b/app/assets/images/google.png new file mode 100644 index 0000000000000000000000000000000000000000..dd2dc639c0f3815a123c89be92896c58d5829076 GIT binary patch literal 2684 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 XeolUv*?iH={~((g$oPTwuLJ)9o%ndw literal 0 HcmV?d00001 diff --git a/app/assets/images/twitter.png b/app/assets/images/twitter.png new file mode 100644 index 0000000000000000000000000000000000000000..c2822dc8e609a644fc763f98248136c6ee05b26a GIT binary patch literal 2830 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 literal 0 HcmV?d00001 diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js index 3e643e87..e3794847 100644 --- a/app/assets/javascripts/admin.js +++ b/app/assets/javascripts/admin.js @@ -5,5 +5,6 @@ // the compiled file. // //= require jquery +//= require jquery_ujs //= require_directory ./work //= require system diff --git a/app/assets/javascripts/omniauth_callbacks.coffee b/app/assets/javascripts/omniauth_callbacks.coffee deleted file mode 100644 index 24f83d18..00000000 --- a/app/assets/javascripts/omniauth_callbacks.coffee +++ /dev/null @@ -1,3 +0,0 @@ -# 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/omniauth_callbacks.css.scss b/app/assets/stylesheets/omniauth_callbacks.css.scss deleted file mode 100644 index 77eaa70e..00000000 --- a/app/assets/stylesheets/omniauth_callbacks.css.scss +++ /dev/null @@ -1,3 +0,0 @@ -// Place all the styles related to the omniauth_callbacks controller here. -// They will automatically be included in application.css. -// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/test.css.scss b/app/assets/stylesheets/test.css.scss index 92bc1787..6a610bf1 100644 --- a/app/assets/stylesheets/test.css.scss +++ b/app/assets/stylesheets/test.css.scss @@ -60,6 +60,26 @@ select.error { position: absolute; } +.auth-servers { + background: #f0f0f0; + padding: 10px; + display: inline-block; + list-style-type: none; +} +.auth-servers li { + float: left; + padding: 10px; + margin: 10px; +} +.twitter { + width: 30%; + background: #f0ffff; +} +.google { + width: 30%; + background: #fff0f0; +} + // hide number field's spinner //for chrome input[type=number]::-webkit-outer-spin-button, diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index d41303c7..a5aea55b 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -1,4 +1,4 @@ -class OmniauthCallbacksController < ApplicationController +class OmniauthCallbacksController < Devise::OmniauthCallbacksController def all user = User.from_omniauth(request.env["omniauth.auth"]) @@ -15,6 +15,10 @@ class OmniauthCallbacksController < ApplicationController self.all end + def google_oauth2 + self.all + end + def failure return render :json => {:success => false, :errors => ["Login failed."]} end diff --git a/app/models/user.rb b/app/models/user.rb index bde3804b..69ca60db 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -6,7 +6,8 @@ class User < ActiveRecord::Base # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, - :omniauthable#, :confirmable + :omniauthable, :omniauth_providers => [:twitter, :google_oauth2] + #, :confirmable def create_token loop do diff --git a/app/views/user_sessions/new.html.erb b/app/views/user_sessions/new.html.erb index 3b1c8eb3..2266c47e 100644 --- a/app/views/user_sessions/new.html.erb +++ b/app/views/user_sessions/new.html.erb @@ -1,27 +1,43 @@ -<% @page_title = t('users.signin.signin') %> -

<%= link_to h(manifest.magic_numbers['profile']['users']['caption']), root_path %>

- - - - - -
-

<%= sanitize manifest.magic_numbers['profile']['users']['description'], :tags => %w(a p img br) %>

-
- <%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %> -
<%= f.label :email %>
- <%= f.email_field :email %>
+<% @page_title = t('users.signin.title') %> +

+ <%= link_to h(manifest.magic_numbers['profile']['users']['caption']), root_path %> + <%= link_to h(manifest.magic_numbers['profile']['users']['caption']), 'll/' %> +

+

+ <%= sanitize manifest.magic_numbers['profile']['users']['description'], :tags => %w(a p img br) %> +

+
+ <%= t('users.signin.notice') %> +
+
    + +
  • + <%= link_to t('users.signin.google.caption'), user_omniauth_authorize_path(:google_oauth2) %> + <%= link_to tag(:img, :src => '/images/google.png'), user_omniauth_authorize_path(:google_oauth2) %> +

    <%= t('users.signin.google.notice') %>

    +
  • +
+
+
+
<%= f.submit t('users.signin.form.signin') %>
+ <% end %> + <%= render :partial => "/users/shared/links" %> + diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index d64c9044..3f648010 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -7,6 +7,7 @@ Devise.setup do |config| config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com" config.sign_out_via = :get config.omniauth :twitter, Rails.application.secrets.twitter_api_key, Rails.application.secrets.twitter_api_secret + config.omniauth :google_oauth2, Rails.application.secrets.google_api_key, Rails.application.secrets.google_api_secret config.secret_key = Rails.application.secrets.devise_secret # Configure the class responsible to send e-mails. # config.mailer = "Devise::Mailer" diff --git a/config/locales/pettanr.ja.yml b/config/locales/pettanr.ja.yml index eb2d325b..1188ba41 100644 --- a/config/locales/pettanr.ja.yml +++ b/config/locales/pettanr.ja.yml @@ -713,7 +713,16 @@ ja: signup: 登録する signin: title: ログイン - signin: ログイン + notice: 下記のサービスのアカウントを使って認証できます。 + twitter: + caption: Twitterアカウントを使って認証する + notice: 別途メールアドレスの入力が必要となります。Twitterで通信を許可した後、エラーメッセージの入った入力フォームが表示されますが、そのままメールアドレスを入力して登録してください。 + google: + caption: googleアカウントを使って認証する + notice: 。 + form: + notice: アカウントを作成してサインイン(開発者向け) + signin: ログイン edit: title: アカウントの設定変更 forgot: diff --git a/config/secrets.yml.org b/config/secrets.yml.org new file mode 100644 index 00000000..178071f9 --- /dev/null +++ b/config/secrets.yml.org @@ -0,0 +1,13 @@ +development: + secret_key_base: xxxxxxxxxxxxxx + devise_secret: yyyyyyyyyyyyyyy + twitter_api_key: twitter-xxxxxxxxxxxxxx + twitter_api_secret: twitter-zzzzzzzzzzzz + aws_access_key_id: aws-aid-zzzzzz + aws_secret_access_key: aws-key-xxxxxx + +test: + secret_key_base: xxxxxxxxxxxxxx + +production: + secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> diff --git a/public/images/google.png b/public/images/google.png new file mode 100644 index 0000000000000000000000000000000000000000..dd2dc639c0f3815a123c89be92896c58d5829076 GIT binary patch literal 2684 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 XeolUv*?iH={~((g$oPTwuLJ)9o%ndw literal 0 HcmV?d00001 diff --git a/public/images/twitter.png b/public/images/twitter.png new file mode 100644 index 0000000000000000000000000000000000000000..c2822dc8e609a644fc763f98248136c6ee05b26a GIT binary patch literal 2830 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 literal 0 HcmV?d00001 -- 2.11.0