【Rails4.2.x】omniauth(twitter/facebook/github)実装まとめ

#はじめに
実装する度に検索するのも面倒なので、この際にまとめます。
理解すべきところはそれについて言及しています。

# 追記

こちらも参考になります。
[Devise+OmniAuthでユーザ認証を実装する手順](http://qiita.com/kami_zh/items/94aec2d94a2b4e9a1d0b)


#対象読者
自分のサービスにソーシャルログインを追加したい人
#[omniauth](https://github.com/intridea/omniauth#integrating-omniauth-into-your-application)とは
Webアプリケーションのログイン認証を、複数のプロバイダを通して行うことができるgemです。
新規登録の際によくある、Twitter・Facebookでログインはこれを使って実装できます。

#事前準備
Userモデルを作成します。
カラム名は使用したいデータに合わせて変更してください。

```ruby:twitter用
rails g model user provider:string uid:string nickname:string image_url:string
```

```ruby:github用
rails g model user name:string provider:string uid:text oauth_token:string
```


#[omniauth-twitter](https://github.com/arunagw/omniauth-twitter)

`Gemfile`に`gem omniauth`, `gem omniauth-twitter`を追加。

```ruby:Gemfile
gem 'omniauth'
gem 'omniauth-twitter'

```

`omniauth.rb`を作成し、以下を記述。

```ruby:config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET']
end
```
middlewareとは？自分で作れるの？と思って[調べました](http://shokai.org/blog/archives/8233)
Webサーバーがフレームワークとデータのやりとりをする場所です。
自作すると色々できておもしろそう。

[Twitter Application Management](https://apps.twitter.com/)でキーを発行します。
そして[direnv](http://qiita.com/kompiro/items/5fc46089247a56243a62)で`.envrc`を作成し、管理します。

```ruby:.envrcを作成
direnv edit .
```

```ruby:.envrcを編集
export TWITTER_KEY="xxxxxxxxxxxxxxxxx"
export TWITTER_SECRET="xxxxxxxxxxxxxxxxxxxxxx"
```
さて、モデルから作成していきます。


```ruby:model/user.rb
class User < ActiveRecord::Base

  def self.find_or_create_from_auth(auth)
    provider = auth[:provider]
    uid = auth[:uid]
    nickname = auth[:info][:nickname]
    image_url = auth[:info][:image]

    self.find_or_create_by(provider: provider, uid: uid) do |user|
      user.nickname = nickname
      user.image_url = image_url
    end
  end
end
```

`find_or_create_from_auth`はDBにユーザーがいれば持ってきて、なければ作るという処理です。

次にsessionsコントローラーを作成し、以下を記述します。


```ruby:controller/sessions_controller.rb
class SessionsController < ApplicationController
  def create
    user = User.find_or_create_from_auth(request.env['omniauth.auth'])
    session[:user_id] = user.id
    redirect_to root_path
  end

  def destroy
    reset_session
    redirect_to root_path
  end
end
```

次に、`application_controller.rb`にヘルパーメソッドを記述していきます。


```ruby:application_controller.rb
class ApplicationController < ActionController::Base

  protect_from_forgery with: :exception

  helper_method :current_user, :logged_in?

  private

  def current_user
    return unless session[:user_id]
    @current_user ||= User.find(session[:user_id])
  end

  def logged_in?
    !!session[:user_id]
  end

  def authenticate
    return if logged_in?
    redirect_to root_path, alert: 'ログインしてください'
  end
end
```
`helper_method :メソッド名`で指定すると、そのメソッドがViewでも扱えるようになります。
ユーザーの識別やログインしているかどうかを返すメソッドを指定するとViewがすっきりします。
`logged_in?`の`!!`は返り値が`nil`の場合、`nil`ではなく`false`を返すようにしています。
メソッド名が真偽値であるため、`false`で帰ったほうがより良いと思います。

次に、`route.rb`にログイン・ログアウトのルーティングを設定します。

```ruby:config/route.rb
Rails.application.routes.draw do
  get 'auth/:provider/callback', to: 'sessions#create'
  get '/logout', to: 'sessions#destroy'

  root 'homes#top'
end
```

最後に、Viewにログイン・ログアウト用のリンクを記述し、終了です。

```ruby:views/homes/top.html.slim
- if logged_in?
  = link_to 'ログアウト', logout_path
- else
  = link_to 'Twitterでログイン', 'auth/twitter'
```

401が発生する場合は、twitter developerの方でアクセストークンを発行し直したり、
`.envrc`に不必要な空白がはいってないか確認してください。(僕はこれでした)
#facebook-omniauth

[試してないけど参考にされたい]
(http://www.workabroad.jp/posts/2099)

# instagram-omniauth

[30分でinstagram-omniauth + Devise でログイン機能を実装してみる]
(http://www.hassansan.me/entry/2017/09/15/000826)

#github-omniauth

[Github developer settings](https://github.com/settings/developers)
上記のリンクでアプリケーションを登録します。
Homepage Urlには`http://localhost:3000`
Authorization callback URLには`http://localhost:3000/auth/github/callback`と入力しましょう。
実際の運用では、本番環境用のURLを新規に作ることをオススメします。

```ruby:Gemfile
gem 'omniauth'
gem 'omniauth-github'
```
```ruby:initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'], scope: "user,repo"
end
```
データ取得のscope指定はサービスに合わせて設定します。

```ruby:model/user.rb
def self.create_with_omniauth(auth)
  create! do |user|
    user.provider = auth['provider']
    user.uid = auth['uid']
    user.name = auth['info']['nickname']
    user.oauth_token = auth['credentials']['token']
  end
end
```

```ruby:controllers/sessions_controller.rb
class SessionsController < ApplicationController
  def callback
    auth = request.env['omniauth.auth']

    user = User.find_by(provider: auth['provider'], uid: auth['uid']) || User.create_with_omniauth(auth)
    session[:user_id] = user.id
    redirect_to root_path
  end

  def destroy
    reset_session
    redirect_to root_path
  end
end
```

```ruby:application_controller.rb
class ApplicationController < ActionController::Base

  protect_from_forgery with: :exception

  helper_method :current_user, :logged_in?

  private

  def current_user
    return unless session[:user_id]
    @current_user ||= User.find(session[:user_id])
  end

  def logged_in?
    !!session[:user_id]
  end

  def authenticate
    return if logged_in?
    redirect_to root_path, alert: 'ログインしてください'
  end
end
```

```ruby:config/route.rb
Rails.application.routes.draw do
  get 'auth/:provider/callback', to: 'sessions#callback'
  get '/logout', to: 'sessions#destroy'

  root 'homes#top'
end
```

```ruby:views/top.html.slim
- if logged_in?
  = link_to 'Sign out', '/signout'
- else
  = link_to 'Githubアカウントでログイン', '/auth/github'

```

# 最後に
ログインはちょちょいと乗り越えて、サービスの核を作りこみましょう！