8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RUNTEQAdvent Calendar 2024

Day 22

【Rails】なぜSorceryは、LINEログインからメールアドレスを取得できないか

Last updated at Posted at 2024-12-17

はじめに

本記事で分かること

  • Sorceryを使っても、LINEログインからユーザーのメールアドレスを取得できない理由
  • LINEログインのフロー
  • LINEログインからメールアドレスを取得するために必要なこと

結論

LINEログインでユーザーのメールアドレスを取得するには、IDトークンの検証が必要。この検証処理がSorceryに含まれていないので、LINEログインからメールアドレスを取得することができない。

現在はSorceryはメンテナンスが行われていないため、Sorceryの使用には注意してください

対象

  • Sorceryを使ってLINEログインを実装しようとされている方

環境

  • Sorcery v0.17.0
  • LINEログイン v2.1
  • Ruby on Rails v7.1

目次

  1. LINEログインのフロー
    1-1. LINEログインの全体像
    1-2. LINEログインからユーザーのメールアドレスを取得する方法
  2. SorceryはどのようにLINEログイン処理を行うか
    2-1. Sorceryの前提知識
    2-2. Sorceryのソースコードを読んでみる
    2-3. @user_hashの中身を見てみる
    2-4. IDトークンを手動でLINEログインに送り、検証してみる
  3. SorceryはIDトークンを検証してくれるのか?
    (補足) IDトークンを手動でLINEログインに送り、検証してみる
  4. まとめ
  5. 参考資料

1. LINEログインのフロー

1-1. LINEログインの全体像

image.png
(転載:https://developers.line.biz/ja/docs/line-login/integrate-line-login/#login-flow)

上図に沿って、一つずつ解説します。
ここでのポイントは、「手順10(下方)でアクセストークンと共に、IDトークンもLINE PlatformからWebアプリに送られること」 です。

  1. 【A. ユーザー】LINEログインを開始する
  2. 【B. Webアプリ】ユーザーをLINE Platformの認可URLにリダイレクトする
    • このURLパラメータには、redirect_uristateを含める
      • redirect_uri:認証・認可完了後に、ユーザーがリダイレクトされるWebアプリのURL
      • state:CSRFを防ぐためのランダムな文字列
  3. 【C. LINE Platform】LINEログイン画面をユーザーに表示する
  4. 【A. ユーザー】認証する(LINEにログインする)
  5. 【C. LINE Platform】認可画面をユーザーに表示する
  6. 【A. ユーザー】認可する
  7. 【C. LINE Platform】ユーザーを、redirect_uriにリダイレクトする。その際、下記2つの情報がredirect_uriのパラメータに付加される
    • state:手順2でLINE PlatformがWebアプリから受け取ったもの
    • authorization code:ユーザーが認可された際に作られるコード。Webアプリはこれを用いて、LINE Platformからアクセストークンを取得する。
  8. 【B. Webアプリ】手順2でLINE Platformに送ったstateと、手順7でLINE Platformから受け取ったstateが同一であるか検証する
  9. 【B. Webアプリ】LINE Platformに、authorization codeを送り、アクセストークンをリクエストする
  10. 【C. LINE Platform】authorization codeを検証し、Webアプリにアクセストークンを送る
    • 図には載っていませんが、この時にアクセストークンと共にIDトークンも送られます。
  11. 【B. Webアプリ】アクセストークンを使い、LINE Platformにユーザー情報をリクエストする
  12. 【C. LINE Platform】アクセストークンを検証し、Webアプリにユーザー情報を送る

1-2. LINEログインからユーザーのメールアドレスを取得する方法

まず、ユーザーのメールアドレスを取得するためには、以下の3つの準備が必要です。

  1. メールアドレスの取得権限の申請
    申請方法はこちらから

  2. ユーザーをリダイレクトする際(手順2)に、scopeパラメーターにemailを指定する
    指定する方法はこちらから

  3. IDトークンの検証

先ほどのログインフローでは、アクセストークンをLINE Platformに送ると、ユーザー情報が取得できるとあります。しかし、ユーザーのメールアドレスは、これでは取得できません。IDトークンの検証によってはじめて取得できる点に注意してください。

では、Sorceryが③IDトークンの検証を行ってくれるのか、見ていきましょう(①取得権限の申請、②scopeパラメーターにemailを指定はできている前提とします)。

2. SorceryはどのようにLINEログイン処理を行うか

2-1. Sorceryの前提知識

Sorceryのインストール方法など、基本的な使い方はこちらを参照してください

Sorceryの外部認証機能(external)については、こちらをご覧ください

2-2. Sorceryのソースコードを読んでみる

SorceryのExternalのソースコードを見てみます。
すると、アクセストークンを使い、user情報を取得するメソッドがあります。

lib/sorcery/controller/submodules/external.rb
def sorcery_fetch_user_hash(provider_name)
  provider = sorcery_get_provider provider_name
  if @provider.nil? || @provider != provider
    @provider = provider
    @access_token = nil
    @user_hash = nil
  end

  @access_token ||= @provider.process_callback(params, session) # sends request to oauth agent to get the token
  @user_hash ||= @provider.get_user_hash(@access_token) # uses the token to send another request to the oauth agent requesting user info
  nil
end

しかし、先ほど確認した通り、メールアドレスの取得には、アクセストークンではなくIDトークンの検証が必要です。
sorcery_fetch_user_hash(provider_name)では、あくまでアクセストークンを使ったユーザー情報の取得を行っているにすぎません。

では、念のため、そこで取得された@user_hashの中身を見てみましょう。

2-3. @user_hashの中身を見てみる

前提として、開発中のRailsアプリに、Externalのドキュメント通りにログイン機能を実装しています。

また、else内の初めにbinding.pryを設置し、処理を止めています。

app/controllers/oauths_controller.rb
  def callback
    provider = params[:provider]
    if @user = login_from(provider)
      redirect_to root_path, :notice => "Logged in from #{provider.titleize}!"
    else
      begin
        binding.pry # ここでデバッグをします
        @user = create_from(provider)
        reset_session
        auto_login(@user)
        redirect_to root_path, :notice => "Logged in from #{provider.titleize}!"
      rescue
        redirect_to root_path, :alert => "Failed to login from #{provider.titleize}!"
      end
    end
  end

なお、デバッグした時点で、既にlogin_from(provider)が実行されています。そこには、sorcery_fetch_user_hashが含まれるので、これも実行済みです(下記ソースコードをご参照ください)。

lib/sorcery/controller/submodules/external.rb
def login_from(provider_name, should_remember = false)
    sorcery_fetch_user_hash provider_name
    ...

では、コンソール上で@user_hashの中身を見てみます。

@user_hash
=> {:token=>
"hoge",
:refresh_token=>"fuga",
:expires_at=>1727252560,
:expires_in=>1727252560,
:user_info=>
   {"userId"=>"aaa",
    "displayName"=>"山田太郎",
    "pictureUrl"=>
     "https://profile.line-scdn.net/xxx"},
  :uid=>"aaa"}

※個人情報が含まれるため、一部値を変更しています。

やはり、@user_hashにはメールアドレスが入っていませんでした。

2-4. IDトークンを手動でLINEログインに送り、検証してみる

1-1. LINEログインの全体像にて、アクセストークンと共に、IDトークンも送られると説明しました。
そのIDトークンは受け取れているのでしょうか?
同じ個所でbinding.pryで処理を止めたまま、access_tokenを確認してみます。

access_token
=> #<OAuth2::AccessToken:xxx
...
  @params=
   {"token_type"=>"Bearer",
    "scope"=>"profile openid",
    "id_token"=>         # ここにIDトークンがある
     "fuga",
    "token_url"=>"https://api.line.me/oauth2/v2.1/token"},
...

すると、IDトークンは、アクセストークンと共に取得できているのが分かります。

3. SorceryはIDトークンを検証してくれるのか?

では、Sorcery(External)は、そのIDトークンを検証する処理を含むのでしょうか。
答えはNOです(少なくとも私は、externalのソースコードを読み解いても、IDトークンを検証する処理は、見つかりませんでした・・・)

(補足) IDトークンを手動でLINEログインに送り、検証してみる

なお、curlコマンドを使い、手動でIDトークンをLINE Platformに送り検証すると、メールアドレスが取得できることが確認できました。このことから、LINE Platform側には問題がないことが分かります。

# curlコマンドでIDトークンを検証してみる
curl -v -X POST 'https://api.line.me/oauth2/v2.1/verify' \
  -d 'id_token=fuga' \
  -d 'client_id=xxx'
# メールアドレスが返ってきた

{"iss":"https://access.line.me",(省略),"name":"山田太郎","email":"taro@gmail.com"}

4. まとめ

  1. LINEログインからメールアドレスを取得するには、IDトークンの検証が必要
  2. SorceryにはIDトークンの検証処理は含まれていない
  3. したがって、Sorceryを使うだけではLINEからユーザーのメールアドレスを取得できない。別途自分で処理を実装するなどして、工夫が必要

5. 参考資料

8
2
0

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
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?