Posts Rails 搭配 Omniauth line
Post
Cancel

Rails 搭配 Omniauth line

我是wells,擔任過室內配線的國手,征服了電氣領域後,現在正跨大版圖到資訊界。

本篇內容

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

前置作業

1. 建立新檔案

使用 Rails 指令新增專案:

1
2
rails new demo
cd demo

2. 產生空白頁

使用 Rails 指令新增 Controller:

1
rails g controller home

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

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

1
root to: "home#index"

安裝相關套件

Gemfile 中加入以下內容:

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

加入後執行 bundle

生成 User Model

請執行以下指令:

1
2
3
rails g devise:install
rails g devise user
rails g migration add_line_login

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

1
2
3
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:

1
rails g migration resolve_users_email_unique

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

1
2
3
4
5
6
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:

1
rails db:migrate

設定 Line Login

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

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

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

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

picture 2

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

picture 3

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

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

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

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

1
2
3
4
devise :database_authenticatable, :registerable,
-        :recoverable, :rememberable, :validatable
+        :recoverable, :rememberable, :validatable,
+       :omniauthable, omniauth_providers: [:line]

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

1
2
3
4
5
6
7
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 中加入以下內容:

1
2
3
4
5
6
7
def email_required?
    false
end

def password_required?
    false
end

新增 omniauth controller

1
rails g controller OmniauthCallbacks

並在其中填入以下內容:

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

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

1
2
3
4
-   devise_for :users
+   devise_for :users, controllers: {
+        omniauth_callbacks: 'omniauth_callbacks'
+    }

新增身份驗證

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

1
2
3
4
5
6
include Rails.application.routes.url_helpers

def authenticate_user
    return if current_user.present?
    redirect_to user_line_omniauth_authorize_path
end

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

1
before_action :authenticate_user

Devise 預設是 before_action :authenticate_user!

picture 4

實作登入登出

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

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

1
2
3
4
5
6
7
8
9
10
11
 <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>

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

已登入時可以登出: picture 2

參考資料

This post is licensed under CC BY 4.0 by the author.

Rails 實作儲存型 XSS

Rails Deploy 筆記