##チーム課題を個人でやってみるけん_1
###作成の経緯
チームのブランチで作業する前に確認したい部分があるので
実験的に個人で新たなapp作成を行い作ってみる。
今回試みるのは、セッション
とAPI
実装部分
実装完了の定義(実装したいことと現状)
(現状)
2020/1/1時点ではSNSで新規登録するとユーザー登録画面に遷移しない
(実現したいこと)
SNSで新規登録時、名前やアドレスの情報を保持したままユーザー登録画面に遷移する
大まかな部分は実装で動くのは確認しているが
1月2日に実装部分に不備があったので修正と更新しました。
今回実装確認(変更)したい箇所は
app/models/user.rb
の登録時の条件分岐箇所と
views/devise/registrations/new.html.haml
form_forのurl記述とrender部分
セッションの記述方法は別の方法もあるので今回の方法でうまく動かなかったら
今回と別の方法も考えています
(追記)
セッションとAPIを同時に進めたら事故ったのでセッションは今回作ったAPI実装のアプリに追記で作る方法に切り替えます!
※バリデーションに関しては詰めていないので修正は後ほど行う予定ですが、今の時点でやるべきか?
記述しました_φ(・_・
####アドバイス等いただけるとすごくありがたい!!!
今回ディレクトリ名は「mi_sns」にしました
$ rails _5.2.4_ new mini_sns -d mysql
$ cd mi_sns
$ rails db:create
$ rails g controller users
root 'users#index' # ログインor新規登録を選ぶページ
resources :users, only: :new # 新規登録方法を選ぶページ
gem 'devise'
gem 'haml-rails'
$ bundle install
$ rails g devise:install
$ rails g devise user
$ rails g devise:views
$ rails haml:erb2haml
カラムは各々必要なものを設定してください
# frozen_string_literal: true
class DeviseCreateUsers < ActiveRecord::Migration[5.2]
def change
create_table :users do |t|
t.string :nickname, null: false, default: ''
t.string :firstname, null: false, default: ''
t.string :lastname, null: false, default: ''
t.string :kana_firstname, null: false, default: ''
t.string :kana_lastname, null: false, default: ''
t.date :birthday, null: false
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
#以下省略
$ rails db:migrate
追加したカラムの入力欄を用意
%h2 Sign up
= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|
= render "devise/shared/error_messages", resource: resource
.field
= f.label :nickname
%br/
= f.text_field :nickname, autofocus: true
.field
= f.label :email
%br/
= f.email_field :email, autofocus: true, autocomplete: "email"
-# - if @sns_id.present?
-# = hidden_field_tag :sns_auth, true
-# - else
.field
= f.label :password
- if @minimum_password_length
%em
(#{@minimum_password_length} characters minimum)
%br/
= f.password_field :password, autocomplete: "new-password"
.field
= f.label :password_confirmation
%br/
= f.password_field :password_confirmation, autocomplete: "new-password"
.actions
.field
= f.label :firstname
%br/
= f.text_field :firstname
.field
= f.label :lastname
%br/
= f.text_field :lastname
.field
= f.label :kana_firstname
%br/
= f.text_field :kana_firstname
.field
= f.label :kana_lastname
%br/
= f.text_field :kana_lastname
.field
= f.label :birthday
%br/
= f.date_select :birthday, start_year: 1950, end_year: 2019
= f.submit "Sign up"
= render "devise/shared/links"
追加したカラムを受け取るストロングパラメータの設定
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
private
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname, :firstname, :lastname, :kana_firstname, :kana_lastname, :birthday])
end
end
最後に確認用のビューを作成
- if user_signed_in?
= "#{current_user.nickname}でログイン中"
= link_to 'ログアウト', destroy_user_session_path, method: :delete
- else
ログインしていません
= link_to '新規登録', new_user_path
= link_to 'ログイン', new_user_session_path
= link_to 'メールアドレスで登録', new_user_registration_path
= link_to 'Facebookで登録', user_facebook_omniauth_authorize_path, method: :post
= link_to 'Googleで登録', user_google_oauth2_omniauth_authorize_path, method: :post
###OmniAuthによるSNS認証の実装
参考
・omniauth-rails_csrf_protectionについて
・公式ドキュメント
・RailsでFacebookとGoogleのOAuth連携。SNS認証の方法
・[Rails5] Device SNS認証後に独自のユーザ登録フォームを実装する方法
・Rails5 APIで認証付きのWebAPIを作ってみる
・[Rails] Facebook/Twitter/Googleでのユーザー登録をDevise & Omniauthを使って爆速で実装する
####Gemを導入
gem 'omniauth-facebook'
gem 'omniauth-google-oauth2'
gem "omniauth-rails_csrf_protection"
$ bundle install
####APIキーの環境変数設定
$ vim ~/.bash_profile
``
i→エンターで下をコピペ
```ruby:~/.bash_profile
export FACEBOOK_CLIENT_ID='メモしたアプリID'
export FACEBOOK_CLIENT_SECRET='メモしたapp secret'
export GOOGLE_CLIENT_ID='メモしたクライアントID'
export GOOGLE_CLIENT_SECRET='メモしたクライアントシークレット'
記入後はesc
→:wq
エンター
$ source ~/.bash_profile
アプリ側に読み込む記述をする
config.omniauth :facebook,ENV['FACEBOOK_CLIENT_ID'],ENV['FACEBOOK_CLIENT_SECRET']
config.omniauth :google_oauth2,ENV['GOOGLE_CLIENT_ID'],ENV['GOOGLE_CLIENT_SECRET']
####SNS認証に必要なモデル、コントローラを用意
「SNS認証とユーザー登録のタイミングが異なる」仕様にするため、SNSのuid
やprovider
を保存するためのテーブルを作成する
Userモデルとのアソシエーションのために、外部キーとしてuser_id
を持たせる
$ rails g model sns_credential provider:string uid:string user:references
$ rails db:migrate
#変更前
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
end
#変更後
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :omniauthable, omniauth_providers: [:facebook, :google_oauth2]
has_many :sns_credentials
end
#変更前
class SnsCredential < ApplicationRecord
belongs_to :user
end
#変更後
class SnsCredential < ApplicationRecord
belongs_to :user, optional: true
end
#1/8追記
#このoptional: trueがないと外部キーのnilを許可できなくなっちゃうよ
#この記述忘れてひどく苦しんだ・・・
参考ページ
optional: trueってなに
deviseのコントローラをカスタマイズするためのコントローラを作成
$ rails g devise:controllers users
deviseが提供するアクションの内容をカスタマイズすることができるようになった。
#変更前
Rails.application.routes.draw do
devise_for :users
#以下省略
#変更後
Rails.application.routes.draw do
devise_for :users, controllers: {
sessions: 'users/sessions',
omniauth_callbacks: 'users/omniauth_callbacks',
registrations: 'users/registrations'
}
#以下省略
環境変数の読み込みができているか確認
rails c
$ rails c
irb(main):001:0> ENV['FACEBOOK_CLIENT_ID']
=> "1111111111111111" # 数字だけ
irb(main):002:0> ENV['FACEBOOK_CLIENT_SECRET']
=> "1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1" # 数字と文字列
irb(main):003:0> ENV['GOOGLE_CLIENT_ID']
=> "~~~~~~~googleusercontent.com" # 末尾がこの形
irb(main):004:0> ENV['GOOGLE_CLIENT_SECRET']
=> "1a1a1a1a1aa1_1a1a1a1_1a" # 英数字と_(アンダーバー)が含まれる
###SNS認証を実現するためのメソッドを実装
OmniAuthのGithub
google-oauth2のGithub
def facebook
authorization
end
def google_oauth2
authorization
end
def failure
redirect_to root_path
end
private
def authorization
#User.from_omniauthメソッドの中身はuser.rbに記述する
# @userに代入することでdeviceのヘルパーを使える
@user = User.from_omniauth(request.env["omniauth.auth"])
if @user.persisted? #ユーザー情報が登録済みなので、新規登録ではなくログイン処理を行う
sign_in_and_redirect @user, event: :authentication
else #ユーザー情報が未登録なので、新規登録画面へ遷移する
render template: 'devise/registrations/new'
end
end
#下に追記
def self.from_omniauth(auth)
sns = SnsCredential.where(provider: auth.provider, uid: auth.uid).first_or_create
# sns認証したことがあればアソシエーションで取得
# 無ければemailでユーザー検索して取得orビルド(保存はしない)
user = sns.user || User.where(email: auth.info.email).first_or_initialize(
nickname: auth.info.name,
email: auth.info.email
)
# userが登録済みの場合はそのままログインの処理へ行くので、ここでsnsのuser_idを更新しておく
if user.persisted?
sns.user = user
sns.save
end
user
end
#記述変更
- if devise_mapping.omniauthable?
- resource_class.omniauth_providers.each do |provider|
= link_to "#{OmniAuth::Utils.camelize(provider)}でログイン", omniauth_authorize_path(resource_name, provider), method: :post, target: :_blank
%br/
###SNS認証時にはパスワード入力を不要にする
#記述を一部変更
if user.persisted?
sns.user = user
sns.save
end
#SNS認証の時だけパスを自動生成する
{ user: user, sns: sns }
end
返す値が変わったので、コントローラでも代入する部分を修正
# 変更前
@user = User.from_omniauth(request.env["omniauth.auth"])
if @user.persisted?
sign_in_and_redirect @user, event: :authentication #this will throw if @user is not activated
else
render template: 'devise/registrations/new'
end
#変更後
sns_info = User.from_omniauth(request.env["omniauth.auth"])
@user = sns_info[:user]
if @user.persisted? #ユーザー情報が登録済みなので、新規登録ではなくログイン処理を行う
sign_in_and_redirect @user, event: :authentication#this will throw if @user is not activated
else #ユーザー情報が未登録なので、新規登録画面へ遷移する
#@sns_idという変数を作って新規画面で使う
@sns_id = sns_info[:sns].id
render template: 'devise/registrations/new'
end
end
ビューのパスワード入力欄の表示/非表示を分ける
.field
= f.label :email
%br/
= f.email_field :email, autofocus: true, autocomplete: "email"
#この辺りに↓↓↓↓これを入れて
-# - if @sns_id.present?
-# = hidden_field_tag :sns_auth, true
-# - else
.field
= f.label :password
- if @minimum_password_length
%em
(#{@minimum_password_length} characters minimum)
%br/
= f.password_field :password, autocomplete: "new-password"
#コメントアウトを外す
インデントはこんな感じ(ミスると何も表示されなくなる。悲しい)
- if @sns_id.present?
= hidden_field_tag :sns_auth, true
- else
.field
= f.label :password
- if @minimum_password_length
%em
(#{@minimum_password_length} characters minimum)
%br/
= f.password_field :password, autocomplete: "new-password"
.field
= f.label :password_confirmation
%br/
= f.password_field :password_confirmation, autocomplete: "new-password"
.field
= f.label :firstname
%br/
= f.text_field :firstname
.field
= f.label :lastname
%br/
= f.text_field :lastname
.field
= f.label :kana_firstname
%br/
= f.text_field :kana_firstname
.field
= f.label :kana_lastname
%br/
= f.text_field :kana_lastname
.field
= f.label :birthday
%br/
= f.date_select :birthday, start_year: 1950, end_year: 2020
.actions
paramsは通常devise/registrations#create
アクションへ送信される
コントローラをオーバーライドするためにcontrollers/users/registrations_controller.rb
を作成してあるのでこのコントローラ内でcreateアクションの動作を変更する
#こんな感じの部分を
# POST /resource
# def create
# super
# end
# 以下コメントアウト部分を外してこう!
def create
if params[:sns_auth] == 'true'
pass = Devise.friendly_token
params[:user][:password] = pass
params[:user][:password_confirmation] = pass
end
super
end
super
メソッド・・・親モデルの同名メソッドをそのまま実行
アプリケーションをGithubと連携させるためにIDとかの大事な情報を保護!
*1/4更新
下記記述はGemで'dotenv-rails'を記述して設定を
した時に有効なようです。
今回の場合はvim ~/.bash_profileで記述を
しているので下記の設定は不要のようです
[Ruby on Rails]環境変数の設定方法(.bash_profile、Dotenv-rails)
.env
この辺までで、APIの実装は大方できたので(実装続けつつ更新づるかもですが)セッションの実装うつります。