51
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

新規開発や新技術の検証、導入にまつわる記事を投稿しよう!

Rails7でLIFFを用いたLINEログインのハンズオンを作ってみた

Last updated at Posted at 2023-07-09

はじめに

Railsの7系にLIFF(Line Front-end Framework)を組み込んだアプリを開発したのですが、下記の点からかなり苦戦しました。

・LIFF特有の構文への自身の不慣れさ
・Rails7ではアセット管理の方法が様々な種類がある
・RailsでLIFFを活用するための参考資料の不足

そこで今回は、Railsを使用してLINEを組み込んだアプリを作成したい方に向けて、LIFFの活用方法について解説する記事を作成しました。
この記事では、実際の手順に沿ってハンズオン形式で学習を進めていきます。
参考にしていただければ幸いです。

今回作成するのは、トップページに設置したボタンを押すだけでログインができ、ログイン前後でページの内容が切り替わるというシンプルなアプリケーションです。

記事を通じて以下のトピックに触れていきます:

・LIFF(Line Front-end Framework)
・LINEログイン
・esbuild
・Hotwire
・Session
・OAuth
・mkcert(SSL通信)

※私自身が未経験のため、説明に誤りがある可能性もございます。もし記事の内容に誤りや改善の余地があると感じられた場合は、お手数ですがご指摘いただければ幸いです。

作成物のイメージ

本記事で取り上げるアプリケーションの例は、「Sunkissing」という、日焼け止めの塗り直し通知アプリです。

LIFFを使ってLINEログインの実装もしております。
そのため、LINEログイン機能の実装を検討している方にもこちらのハンズオンは役に立つと思います。

本アプリのURLをLINEのトーク画面に送り、スマートフォンのLINEアプリから開いてみてください。PCよりもスマートフォンのLINEアプリから開いた方が、よりスムーズに動作するので是非お試しください。

今回のハンズオンのコードはこちらです。
https://github.com/mayuyuyuyunn/liff_sample_app

使用技術

Rails 7.0.4
ruby 3.1.2
Nodejs v20.3.1
LIFF(LINE Front-end Framework) v2.22.2
mkcert v1.4.4

LIFFとは

LIFFは、LINE Front-end Frameworkの頭文字を取ったものです。

LINE Front-end Framework(LIFF)は、LINEが提供するウェブアプリのプラットフォームです。このプラットフォームで動作するウェブアプリを、LIFFアプリと呼びます。

LIFFとはLINEアプリ上で動作するブラウザのことです。
LINEのトーク画面でURLを開くと、ChromeやSafariなどの外部ブラウザが開きますが、LIFFアプリのURLを開くと、LINEアプリ内部でブラウザが開きます。
またLIFFアプリは、他のブラウザで使用することもでき、登録しているメールアドレスやユーザーIDなど、LINEの情報を取得できるアプリとなります。

LIFFを使用する主なメリットは以下の3つです:

  • LINEログインが使用できる
  • LINEのトーク画面からLIFFアプリを開くと、初回を除いて自動的にログイン処理が行われる
  • ユーザーID、友だち情報、アイコン写真など、LINEの情報を利用できる

LINEログインとは

無料で利用できるLINEアカウントを使用したソーシャルログインサービスです。
初回登録時にユーザー名やパスワードを考えて入力する手間がないため、ストレスフリーです。
詳細な情報はLINEログインの概要をご覧ください。

準備編

Railsプロジェクト作成

Railsの7系で作成しておきます。

$ mkdir liff_sample_app
$ cd liff_sample_app
$ rails _7.0.4_ new . --javasctipt=esbuild --skip-hotwire --css=bootstrap

Rails newのオプションについて説明します。(分かる方は飛ばしてください。)

  • --javascript=esbuild
    JavaScriptのオプションで、esbuildを指定しています。

  • --skip-hotwire
    Rails 7からデフォルトとなったフレームワーク、Hotwireに関するオプションです。Hotwireを使うと、JavaScriptを書かなくてもSPAのようなページを作成できます。今回のアプリでは利用しないのでスキップします。
    今後開発をする中で、意図する挙動をしない場合は、Hotwireが原因の可能性もあります。後々開発で助かることもあるかもしれないので、頭の片隅に入れておきましょう。

  • --css=bootstrap
    CSSのフレームワークとしてBootstrapを指定します。Bootstrapを導入することで、簡単に見た目を整えることができます。

mkcertの導入

ローカル開発環境でSSL/TLS証明書を作成するためのツール、mkcertを導入します。

ローカルサーバーを起動するときのURLは通常http://localhost:3000となりますが、LINE DevelopersコンソールではコールバックURLの指定にhttpは受け入れられません。
したがって、LIFF開発ではURLをhttpsにする必要があります。

mkcertのインストールをします。今回は、Homebrewで入れていきます。

$ brew install mkcert
$ mkcert -install

mkcertを使用して、SSL証明書を発行するための証明証を作成します。

$ mkcert localhost

localhost-key.pemlocalhost.pemという2つのファイルが生成されたかと思います。
次に、configディレクトリ内にcertsディレクトリを作成し、生成した2つのファイルを移動します。
これらのファイルはGitHubにアップロードしないように、.gitignore/config/certs/*を追記してください。

次に、config/puma.rbの設定を行います。
ここでは3000番のポートで起動する記述port ENV.fetch("PORT") { 3000 }をコメントアウトし、 mkcertで作成したSSL証明書を指定します。

config/puma.rb
# port ENV.fetch("PORT") { 3000 }  ←元々記載があるのでコメントアウトする
ssl_bind "0.0.0.0", "3000", {
  cert: "config/certs/localhost.pem",
  key:  "config/certs/localhost-key.pem"
}

これでローカルの開発環境がhttpsで起動できるようになりました。

modelとテーブルの作成

次に、Userの情報にline_user_idを設定します。
通常のユーザー認証ではnamepasswordを組み合わせて使用しますが、LINEログインを使用する場合はINEが提供する一意のuser_idを取得して利用することができます。

terminal
$ bundle exec rails g model User line_user_id:string

マイグレーションファイルを以下のように設定します。ここではnullと重複を許容しないように制約を設けます。

db/migrate/XXXXXXXXX_create_user.rb
class CreateUsers < ActiveRecord::Migration[7.0]
  def change
    create_table :users do |t|
      t.string :line_user_id, null: false, unique: true

      t.timestamps
    end
  end
end

マイグレーションを行います。

terminal
$ bundle exec rails db:migrate

マイグレーションが完了したら、同じようにモデルにもバリデーションをかけましょう。

model/user.rb
class User < ApplicationRecord
  validates :line_user_id, presence: true, uniqueness: true
end

controllerとviewの作成

Userに関するコントローラーとviewを作成します。
今回はユーザー作成とログインに関する機能のみ作成するのでnew createアクションを作成します。

terminal
$ bin/rails g controller users new create

ルーティングは下記の通りです。

config/route.rb
Rails.application.routes.draw do
  resource :user, only: %i[new create]
end

ApplicationControllerにログイン関係のヘルパーメソッドを作成していきます。
sessionはログイン機能などで使われるステートフルな通信を保つための仕組みですね。
突然session[:user_id]が出てきて少し困惑する方もいるかと思いますが、session[:user_id]の値を取得できれば、current_userがわかるというイメージがあればOKです。

application_controller.rb
class ApplicationController < ActionController::Base
  helper_method :current_user
  helper_method :login_required
  helper_method :logged_in?

  private

  def current_user
    # @current_userがnilでsession[:user_id]に値が入っている場合、ユーザーを持ってくる
    @current_user ||= User.find(session[:user_id]) if session[:user_id]
  end

  def login_required
    # ログインしていない(current_userが存在しない場合)root_pathに飛ばす
    redirect_to root_path unless current_user
  end

  def logged_in?
    !current_user.nil?
  end
end

ログイン前後で見た目を分けたいのでstatic_pagesコントローラーと該当するページを作成します。

$ bundle exec rails g controller StaticPages top before_login after_login
static_pages_controller.rb
class StaticPagesController < ApplicationController
  def top
    if current_user.nil?
      render :before_login
    else
      render :after_login
    end
  end

  def before_login; end

  def after_login
    login_required
  end
end
route.rb
Rails.application.routes.draw do
  root 'static_pages#top'
  get '/after_login', to: 'static_pages#after_login'
  resource :user, only: %i[new create]
end

before_loginとafter_loginのviewファイルを書きます。

before_login.html.erb
<div class="container">
  <div class="jumbotron">
    <h1 class="p-3 mb-2 bg-primary text-white">ログイン前のページです</h1>
    <p>This is the homepage of my awesome website.</p>
    <%= link_to '(LINEユーザー登録ログイン', new_user_path, class: 'btn btn-success btn-lg' %>
  </div>
</div>
after_login.html.erb
<div class="container">
  <div class="jumbotron">
    <h1 class="p-3 mb-2 bg-danger text-white">ログイン後のページです</h1>
    <p>This is the homepage of my awesome website.</p>
  </div>
</div>

$ bin/devでサーバーを起動して、https://localhost:3000/にアクセスしてみてください。
before_loginの画面がでます。「ユーザー登録・ログイン」ボタンをクリックしてhttps://localhost:3000/user/newに遷移してればOKです。
確認できたら、ctrl + cでサーバーを止めておいてください。

下準備編、次がラストです!
LIFFの処理はJavaScriptのファイルに書くので、JSファイルを作成していきます。

app/javascript/new.jsを作成してください。
デバックのために、console.log('new');と書いておいてください。

javascript/new.js
console.log('new');

new.jsusers/new.html.erbで読み込ませます。

users/new.html.erb
<p>Loading・・・</p>
<p>※時間がかかることがございます。</p>
<%= javascript_include_tag "new", "data-turbo-track": "reload", defer: true %>

再度https://localhost:3000/にアクセスして、ボタンを押すと
https://localhost:3000/user/newにアクセスできるか確認してください。
検証ツールでコンソールを開いて、下記のようにnewの文字が出力されていればOKです。

liff_sample.gif

LINE Developersの設定編

LINE Developersのページを開いて設定していきます。
以下のURLにアクセスし、普段ご自身が使っているアカウントでログインしてください。

コンソール(ホーム)からプロバイダを作成します。
名前は任意ですが、今回は「LIFFサンプル」と命名しておきます。

左側のメニューから「LINEログイン」をクリックして、チャネルを作成します。
liff_sampleのコピー.png

チャネルの種類では「LINEログイン」、プロバイダでは先ほど作成した「LIFFサンプル」、そしてアプリタイプは「Webタイプ」 を選んでください。他の必要な項目はお好きに設定し、契約内容に同意できたら作成ボタンを押してください。

59c35b34dfb0401ff3d1fed2d78ec1f2.png

チャネル基本設定の一番上にある「チャネルID」をメモしておきます。このIDは後で使用します。

0db2bbe583dbb2a99775c61cde76df5f.png

memo.txt
CHANNNEL_ID="チャネルIDの値"

次に、チャネル基本設定の隣のタブ「LINEログイン設定」をクリックし、LINEログインを使用できるように設定します。
コールバックURLにはhttps://localhost:3000/user/newを入力してください。

12c6b1df5f16245c1675f071cc079668.png

さらに隣のタブで「LIFFアプリ」を追加します。

下記の画像を参考にして入力してください。エンドポイントURLはhttps://localhost:3000/user/newと設定してください。

d2463b3015fcf214fe00abffa60af455.png

作成すると、一番上にLIFF_IDが表示されます。この値も後で使用するため控えておいてください。

memo.txt
CHANNNEL_ID="チャネルIDの値"
LIFF_ID="LIFF IDの値"

注意
この2つの値はGitHubなどにあげないように注意してください。
.envなど環境変数を用いてください。

RailsとJavaScriptのコード編

new.jsにLIFFを利用したコードを書いていきますが、その前にLIFFの仕組みについて簡単に説明します。
詳しい仕組みは下記の公式のシーケンス図なので、いつでも確認できるようにしておきましょう。

1927693bfa8cf0d4158562cfadce8238.png

1.LIFFで作成されたアプリのURLを開く
2.LIFF SDKにアクセスして、LIFFの初期化とログイン処理を行う
3.LINE PlatformにアクセスしてLINEログイン処理をする
4.成功した場合、LIFFSDKにID tokenをくださいとリクエストが投げることができる
5.無事に取得できたら、ID tokenと一緒にLINE Platformにアクセスする
6.今回使用したいuser_IDだったり、ユーザー名、アイコン、登録しているメールアドレスなどの情報が取得できる
という仕組みのようです。

矢印が複数あり複雑に見えますが、赤い丸で囲ってあるゴール部分から説明していきます。

ゴールは、自分のサーバー(今回ならRails)からID tokenを含むリクエストをLINEプラットフォームに送信し、user_idemailなどの情報を取得することです。

URLにoauth2とあるように、ここではOAuth認証を利用しています。悪意のあるリクエストを防ぐため、事前にトークンを発行し、そのトークンを添えてサーバーに情報送信の許可を得ます。
OAuthについては以下の記事を参考にしてください。

このプロセスではユーザー、作成したLIFFアプリ、LIFF SDK、アプリケーションサーバー、LINEプラットフォームの5つの要素が登場します。
SDKとはSoftware Development Kitの略称で、LIFF SDKはLIFF開発を簡単に行うために用意されたツールセットです。

詳細に知りたい方は、下記公式をみてください。

まず、LIFF SDKを使用するために、scriptタグで読み込みをします。
下記のコードを記載することで、liff.initなどLIFFで用意されているメソッドを使えるようになります。
application.html.erbにあるbodyタグの中にLIFF SDKを読み込むためのscriptタグ追加してください。

application.html.erb
  <body>
    <%= yield %>
    <script charset="utf-8" src="https://static.line-scdn.net/liff/edge/2/sdk.js"></script>
  </body>

続いてJavaScriptでコーディングします。
ここで先ほどLINE Developers編で作成したLIFF_IDを利用します。

new.js
// DOMが読み込まれたら処理が走る
document.addEventListener('DOMContentLoaded', () => {
 // csrf-tokenを取得
 const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
 // LIFF_ID を定数定義
 const LIFF_ID = "自身のLIFF_IDをコピペする";
 // LIFF_IDを使ってLIFFの初期化
 liff
 .init({
   liffId: LIFF_ID,
   // 他のブラウザで開いたときは初期化と一緒にログインもさせるオプション
   withLoginOnExternalBrowser: true
 })
   // 初期化後の処理の設定
 liff
  .ready.then(() => {
   //  初期化によって取得できるidtokenの定義
   const idToken = liff.getIDToken()
   // bodyにパラメーターの設定
   const body =`idToken=${idToken}`
   // リクエスト内容の定義
   const request = new Request('/user', {
     headers: {
       'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
       'X-CSRF-Token': token
     },
     method: 'POST',
     body: body
   });

   // リクエストを送る
   fetch(request)
   // jsonでレスポンスからデータを取得して/after_loginに遷移する
   .then(response => response.json())
   .then(data => {
     data_id = data
   })
   .then(() => {
     window.location = '/after_login'
   })
 })
})

liffの構文について詳しく知りたい方は、下記リファレンスを確認してください。

続けて、Userコントローラーです。
LINE Developers編で作成したCHANNNEL_IDを入れるのを忘れないようにしてください。

users_controller.rb
class UsersController < ApplicationController
  require 'net/http'
  require 'uri'

  def new
    if current_user
      redirect_to after_login_path
    end
  end

  def create
    id_token = params[:idToken]
    channel_id = "保存したチャネルIDを入れる"
    res = Net::HTTP.post_form(URI.parse('https://api.line.me/oauth2/v2.1/verify'), { 'id_token' => id_token, 'client_id' => channel_id })
    line_user_id = JSON.parse(res.body)['sub']
    user = User.find_by(line_user_id:)
    if user.nil?
      user = User.create(line_user_id:)
    elsif (session[:user_id] = user.id)
      render json: user
    end
  end
end

createアクションは先ほどのシーケンス図で赤い印でゴール部分で説明した部分です。自分のサーバーからLINEプラットフォームにID tokenを引っ提げてリクエストを送っています。
id_token = params[:idToken]でOAuth認証に必要なID tokenを取得しているのですが、これは先ほどnew.jsに書いたコードのおかげで持ってこれています。bodyに情報を入れてリクエストをおくりました。どこの部分か分からない方は、少し戻ってnew.jsのコードを見てください。
net/httpライブラリとurlライブラリを使用して、LineプラットフォームにUser情報を取得するためのリクエストを投げています。
最後に、line_user_idを取得して、初登録の方はline_user_idを保存、すでに登録している方はsession[:user_id]に取得したline_user_idを入れてログイン処理を行っています。

再度、サーバーを起動して新規登録・ログインをしてみましょう!
LINEログインの認証画面に遷移し、ソーシャルログインができるかとおもいます。

最後に

お疲れ様でした!!!
これにて、LIFFを用いたLINEログインのハンズオンは完了です。
LINEは現在日本でいちばん使われているコミュニケーションツールのため、
通知系のアプリを作りたい方はLINEを使用して開発するのが便利で良いのかなと思います。
今回LIFFを使用して個人開発をしてみて、Hotwire、セッション、oAuth、SSLなど様々なことを学習するきっかけになりました。
ハンズオンをしてみて、分からないところがあれば調べて復習するといい勉強になると思うのでぜひやってみてください。
この記事が、個人開発をする上での一助になれば幸いです。
最後までお付き合いいただきありがとうございました。

個人開発のアプリ、是非使ってみてください〜 :sparkles:
https://www.sunkissing.net/

51
32
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
51
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?