Rails 搭配 Omniauth line

本篇內容

  • 前置作業
  • 安裝相關套件
  • 生成 User Model
  • 設定 Line Login
  • 新增身份驗證
  • 實作登入登出

前置作業

1. 建立新檔案

使用 Rails 指令新增專案:

rails new demo cd demo

2. 產生空白頁

使用 Rails 指令新增 Controller:

rails g controller home

之後新增 app/views/home/index.html.erb

最後在 config/routes.rb 中新增以下內容:

root to: "home#index"

安裝相關套件

Gemfile 中加入以下內容:

gem 'devise' gem 'omniauth-line', git: 'https://github.com/etrex/omniauth-line.git' gem "dotenv-rails", "~> 2.7"

加入後執行 bundle

生成 User Model

請執行以下指令:

rails g devise:install rails g devise user rails g migration add_line_login

開啟剛剛新增的 db/migrate/xxxxxxxxx_add_line_login.rb 並在 change 方法中加入以下內容:

add_column :users, :line_id, :string add_column :users, :name, :string add_column :users, :image_url, :string

另外,由於我們之後要以 line_id 區分使用者,這導致 email column 的值可能會重複(因為 Devise 預設 email 的值為 '' ),因此需要解決 email 重複的問題,以下新增一個 migration:

rails g migration resolve_users_email_unique

開啟新增的 db/migrate/xxxxxxxxx_resolve_users_email_unique.rb 更改為以下內容:

def up change_column :users, :email, :string, null: true, default: nil end def down change_column_null :users, :email, false, SecureRandom.uuid end

此段主要為修改 email column,當 user 被建立時,預設值為 nil,以此來避免唯一鍵重複,而 down 中的 SecureRandom.uuid 則是為了避免原本 emailnil 的資料 db rollback 後發生重複的問題。 到了這一步後就可以執行 Database migrate:

rails db:migrate

設定 Line Login

新增 .env 並在其中放入以下內容:

LINE_LOGIN_CHANNEL_ID= 你的 CHANNEL ID
LINE_LOGIN_CHANNEL_SECRET= 你的 CHANNEL SECRET

該資訊可以在 Line Developers 找到,點擊 LINE LOGIN 的 CHANNEL,可以看到以下畫面: image

LINE LOGIN CHANNEL ID,複製該內容貼至 .env

image

LINE LOGIN CHANNEL SECRET,複製該內容貼至 .env(與 LINE LOGIN CHANNEL ID 同頁面的下方)

image

在 Callback URL 部分填入 https://{your-domain-name}/users/auth/line/callback

config/initializers/devise.rbDevise.setup 區塊中新增以下內容:

config.omniauth :line, ENV['LINE_LOGIN_CHANNEL_ID'], ENV['LINE_LOGIN_CHANNEL_SECRET']

app/models/user.rb 開啟 Omniauthable 功能

devise :database_authenticatable, :registerable, - :recoverable, :rememberable, :validatable + :recoverable, :rememberable, :validatable, + :omniauthable, omniauth_providers: [:line]

app/models/user.rb 新增 from_omniauth 方法

def self.from_omniauth(auth) if auth.provider == "line" user = User.find_or_create_by(line_id: auth.uid) user.update(name: auth.info.name, image_url: auth.info.image) user end end

因為 LINE Login 只會傳入 line_id 而沒有 emailpassword,因此 emailpassword 為非必填,在 app/models/user.rb 中加入以下內容:

def email_required? false end def password_required? false end

新增 omniauth controller

rails g controller OmniauthCallbacks

並在其中填入以下內容:

def line user = User.from_omniauth(request.env["omniauth.auth"] ) sign_in user redirect_to root_path end

config/routes.rb 中加入以下內容:

- devise_for :users + devise_for :users, controllers: { + omniauth_callbacks: 'omniauth_callbacks' + }

新增身份驗證

app/controllers/application_controller.rb 中加入以下內容:

include Rails.application.routes.url_helpers def authenticate_user return if current_user.present? redirect_to user_line_omniauth_authorize_path end

之後只需要在先登入才能進行動作的 Controller 中加入以下內容:

before_action :authenticate_user

Devise 預設是 before_action :authenticate_user!

image

實作登入登出

完成至上一步已經可以進行 Line Login,但假如希望登入登出這件事並非強制的話,可以增加登入登出的列表。

app/views/layouts/application.html.erb<body> ... </body> 之中加入以下內容:

<nav> <h1> <% if current_user.present? %> <%= current_user.name %> 您好: <%= link_to "登出", destroy_user_session_path, method: :delete %> <% else %> <%= link_to "登入", user_line_omniauth_authorize_path %> <% end %> <h1> <hr /> </nav>

這樣子就可以在未登入時可以登入: image

已登入時可以登出: image

參考資料