LINE ログインについて調査したまとめ

# 背景

ユーザーへの通知を、メールより開封率の高そうなLINEに切り替えたい

# 検討した方法

- LINE ログイン APIを用いて、ユーザー登録をLINEと連携
- ユーザー登録時に LINE Messaging APIで連携したBotを強制的に友達追加させる

下記サイトのようなイメージ

- https://www.oisix.com/welcome?page=Login1Bean&mi2=shinkitabTopSEO
- https://cs.appnt.me/facebook/page_tab/2?stand_alone=1

# 結論

2017年11月28日時点では、時期尚早と判断。理由は

- Botを自然にかつ強制的に友達追加できない
    - プログラム側でOAuthのコールバックで友達追加されたかを判定する必要がある
    - サービス提供者側が強制的に友達追加させてる感が出るので、ユーザーへの見え方として微妙
- メールアドレスと電話番号を取得できない https://developers.line.me/en/faq/line-login/
- 既にLINE@でサポートを行っている場合、料金体系が難しい。ちょっと検証で使いたくてもProduction環境ではきつい。
    - FreeプランでMessaging APIを有効にすると、LINE@での個人メッセージが送れなくなる
    - LINE@で連絡しつつMessaging APIを有効にするには月額32000円払う必要あり
    - そのため検証目的では別アカウントを作成するのが安いが、当然本番データが2つのアカウントに分散されるのは検証といえども後々きつい
- 企業アカウントでBotと連携するには、LINE社の許可が必要
    - 個人アカウントと偽ってBotを利用することはできるが、LINE社次第では止められるかもしれないというリスクがある
- LINE APIは直近でアップデートが多く、今後仕様がどうなるか分からない https://developers.line.me/en/news/


# 検証で使ったスクリプト

一応残しておく

```bash
#!/bin/bash

set -eux

client_id=
client_secret=
url=


echo "==> authorize with below url <=="
# echo "https://access.line.me/dialog/oauth/weblogin?response_type=code&client_id=$client_id&redirect_uri=$url&state=12345abcde"
# echo "https://access.line.me/dialog/oauth/weblogin?response_type=code&client_id=$client_id&redirect_uri=$url&state=12345abcde&scope=profile%20openid%20email&bot_prompt=aggressive"

echo "https://access.line.me/oauth2/v2.1/authorize?response_type=code&client_id=$client_id&redirect_uri=$url&state=12345abcde&scope=profile%20openid%20email&bot_prompt=aggressive"

echo
echo "==> input code <=="
read -p '>>> ' code

echo

curl -XPOST \
    -d grant_type=authorization_code \
    -d code=$code \
    -d client_id=$client_id \
    -d client_secret=$client_secret \
    -d redirect_uri=$url \
    https://api.line.me/oauth2/v2.1/token \
    | jq .
```

Laravelで使う

```php:app/Services/SocialiteLineProvider.php
<?php

namespace App\Services;

use Illuminate\Support\Arr;
use Laravel\Socialite\Two\AbstractProvider;
use Laravel\Socialite\Two\ProviderInterface;
use Laravel\Socialite\Two\User;

class SocialiteLineProvider extends AbstractProvider implements ProviderInterface
{
    /**
     * The scopes being requested.
     *
     * @var array
     */
    protected $scopes = ['profile', 'openid', 'email'];

    /**
     * The separating character for the requested scopes.
     *
     * @var string
     */
    protected $scopeSeparator = ' ';

    /**
     * The fields that are included in the profile.
     *
     * @var array
     */
    protected $fields = [
        'profile', 'openid', 'email'
    ];

    /**
     * {@inheritdoc}
     */
    protected function getAuthUrl($state)
    {
        return $this->buildAuthUrlFromBase('https://access.line.me/dialog/oauth/weblogin', $state);
    }

    /**
     * {@inheritdoc}
     */
    protected function getCodeFields($state = null)
    {
        $fields = parent::getCodeFields($state);

        $fields['bot_prompt'] = 'normal';

        return $fields;
    }

    /**
     * {@inheritdoc}
     */
    protected function getTokenUrl()
    {
        return 'https://api.line.me/v2/oauth/accessToken';
    }

    /**
     * Get the POST fields for the token request.
     *
     * @param  string  $code
     * @return array
     */
    protected function getTokenFields($code)
    {
        return parent::getTokenFields($code) + ['grant_type' => 'authorization_code'];
    }

    /**
     * {@inheritdoc}
     */
    protected function getUserByToken($token)
    {
        // $fields = implode(',', $this->fields);

        $url = 'https://api.line.me/v2/profile';

        $response = $this->getHttpClient()->get($url, [
            'headers' => [
                'Authorization' => 'Bearer '.$token,
            ],
        ]);

        return json_decode($response->getBody(), true);
    }

    /**
     * {@inheritdoc}
     */
    protected function mapUserToObject(array $user)
    {
        return (new User)->setRaw($user)->map([
            'id' => $user['userId'], 'name' => Arr::get($user, 'displayName'),
        ]);
    }

    /**
     * Set the user fields to request from LINE.
     *
     * @param  array  $fields
     * @return $this
     */
    public function fields(array $fields)
    {
        $this->fields = $fields;

        return $this;
    }
}
```

```php:app/Providers/AppServiceProvider.php
    public function boot()
    {
        Socialite::extend('line', function ($app) {
            $config = $app['config']['services.line'];

            return Socialite::buildProvider(\App\Services\SocialiteLineProvider::class, $config);
        });
    }
```

```php:config/services.php
    'line' => [
        'client_id' => 'hoge',
        'client_secret' => 'fuga',
        'redirect' => env('APP_HOST') . '/auth/oauth/line/callback',
    ],
```

# 参考サイト

- https://teratail.com/questions/74674