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` 則是為了避免原本 `email` 為 `nil` 的資料 `db rollback` 後發生重複的問題。 到了這一步後就可以執行 Database migrate:
rails db:migrate

設定 Line Login

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

LINE_LOGIN_CHANNEL_ID= 你的 CHANNEL ID
LINE_LOGIN_CHANNEL_SECRET= 你的 CHANNEL SECRET
該資訊可以在 [Line Developers](https://developers.line.biz/console/) 找到,點擊 LINE LOGIN 的 CHANNEL,可以看到以下畫面: ![image](https://github.com/jhang-jhe-wei/jhang-jhe-wei.github.com/assets/59594475/b993d79e-5151-4bd5-b459-88238cee5c79)

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

參考資料