LINE Login 超入門


# 概要

この記事はLINE Loginを使った認証の仕組みをNode.jsベースのアプリに実装するチュートリアルです。LINE側での必要な設定をおこない、WebアプリにSDKをインストールしてLINE Loginを組み込んできます。また、ログインと同時にBotをユーザーの友だちに追加するといったLINE独自の機能もカバーしていきます。

# 必要なスキルと環境

- Node.js/Javascriptの基本的な知識

# 開発の流れ

- まず最初にLoginのChannelを作成します。このChannelはユーザーから見たときに、ユーザーのLINEアカウントを利用しようとしているアプリケーションになります。
- 次にWebサービスを作成し、LINE Loginを組み込みます。
- 認証をテストします。
- 認証と同時にBotを友だちに追加する機能を追加します。
- 取得したアクセストークンを使ってAPIにアクセスします。

# 手順

## Channelを作成する

Channelの作成については下記ドキュメントを参照ください。＊なお、アプリタイプでは **Webで使う** を選択してください。
https://developers.line.me/ja/docs/line-login/getting-started/

作成できたらCallback URLを設定します。これはLINEの認証サーバーでの承認が終わった後にリダイレクトする先のURLになります。
![スクリーンショット_2018-03-08_16_11_11.png](https://qiita-image-store.s3.amazonaws.com/0/26079/3daf9607-b3fe-4971-3a93-f1f086eb2557.png "スクリーンショット_2018-03-08_16_11_11.png")

今回はlocalhost上でNode.jsのWebサービスを起動させて、そこにリダイレクトするので http://localhost:5000/callback と指定します。
![スクリーンショット_2018-03-08_16_11_28.png](https://qiita-image-store.s3.amazonaws.com/0/26079/eba0ad8f-f0c8-6862-df6e-40c999467532.png "スクリーンショット_2018-03-08_16_11_28.png")


## Webサービスを作成する

今回作成するWebサービスのソースコード一式を格納するディレクトリを作成し、そのディレクトリに移動します。

```
$ mkdir line-login-bootcamp
$ cd line-login-bootcamp/
```

npm initコマンドでpackage.jsonファイルを作成します。

```
$ npm init --yes
```

必要となるnpmパッケージをインストールします。

```
$ npm install --save express express-session dotenv line-login
```

- express: リクエストの取り扱いを容易にする汎用的なライブラリ
- express-session: セッション情報を保持するためのライブラリ。stateとnonceの値を保持します（詳しくは後述）
- dotenv: .envファイルから環境変数をロードするためのパッケージ（詳しくは後述）
- [line-login](https://github.com/nkjm/line-login): LINE Login APIのNode.js用SDK

環境変数を保存するための.envファイルを作成します。環境変数はChannel Secretのような環境ごとに異なるか、あるいは秘匿性の高い情報を保存するのに利用します。

```shell:/.env
LINE_LOGIN_CHANNEL_ID=あなたのChannel ID
LINE_LOGIN_CHANNEL_SECRET=あなたのChannel Secret
LINE_LOGIN_CALLBACK_URL=あなたのCallback URL
```

このWebサービスのメインスクリプトとなるindex.jsを作成します。このスクリプトではサーバーを起動、そしてSDKをインポートして任意のURLへアクセスしたときに認証フローを開始します。

```javascript:/index.js
"use strict";

require("dotenv").config();

// ライブラリのインポート。
const express = require("express");
const app = express();
const line_login = require("line-login");
const session = require("express-session");
const session_options = {
    secret: process.env.LINE_LOGIN_CHANNEL_SECRET,
    resave: false,
    saveUninitialized: false
}
app.use(session(session_options));

// 認証の設定。
const login = new line_login({
    channel_id: process.env.LINE_LOGIN_CHANNEL_ID,
    channel_secret: process.env.LINE_LOGIN_CHANNEL_SECRET,
    callback_url: process.env.LINE_LOGIN_CALLBACK_URL
});

// サーバー起動設定。
app.listen(process.env.PORT || 5000, () => {
    console.log(`server is listening to ${process.env.PORT || 5000}...`);
});

// 認証フローを開始するためのルーター設定。
app.get("/auth", login.auth());

// ユーザーが承認したあとに実行する処理のためのルーター設定。
app.get("/callback", login.callback(
    (req, res, next, token_response) => {
        // 認証フロー成功時
        res.json(token_response);
    },(req, res, next, error) => {
        // 認証フロー失敗時
        res.status(400).json(error);
    }
));
```

## サーバーを起動

下記のコマンドを実行してサーバーを起動します。

```
$ node index.js
Server is listening to 5000...
```


## 認証のテスト

ブラウザを起動して http://localhost:5000/auth にアクセスしてみてください。ルーター設定で指定されているlogin.auth()によってLINEの認証サーバーへリダイレクトされることが確認できるはずです。

![スクリーンショット 2018-03-08 14.50.33.png](https://qiita-image-store.s3.amazonaws.com/0/26079/e237ea8f-3eb8-f25f-db3b-c630565ec213.png "スクリーンショット 2018-03-08 14.50.33.png")

ログインすると承認画面が表示されます。「権限を確認」のプルダウンメニューを開くと、scopeの設定にもとづいた権限がリストされていると思います。
![スクリーンショット 2018-03-08 14.51.05.png](https://qiita-image-store.s3.amazonaws.com/0/26079/43850579-c4cb-3fde-4cf0-e91fdecc3aef.png "スクリーンショット 2018-03-08 14.51.05.png")



「同意する」をクリックするとWebサービスにリダイレクトされます。現状では「認証フロー成功時」のコードが実行され、取得したトークンの情報がJSON形式で出力されるはずです。


この部分で、実際にはトークンをデータベースに保存したり、自社サービスのアカウントを作成したりといった処理を実行することになるでしょう。
トークンには下記のような情報が含まれます。

- Social APIにアクセスするためのアクセストークン
- アクセストークンをリフレッシュするためのリフレッシュトークン
- LINE ID
- Display Name
- アイコンイメージのURL
- Email *あらかじめ開発者コンソールから利用申請をおこなった上でscopeにemailを追加しておく必要があります。*

## ログインボタンを設置する

先ほどは認証が開始されるURL (http://localhost:5000/auth) に直接アクセスしましたが、より現実的なユースケースとしてログイン画面を作成してログインボタンを設置し、そのボタンをクリックしたら認証が開始されるように変更します。

まずログインボタンのイメージファイルを格納するディレクトリを **public** として作成します。

```
$ mkdir public
```

次にログインボタンの画像をダウンロードします。完全な画像セットは[こちら](https://developers.line.me/media/line-login/login-button/LINE_Login_Button_Image.zip)からダウロードできますが、今回は下記のイメージだけ利用します。

![btn_login_base.png](https://qiita-image-store.s3.amazonaws.com/0/26079/d921f96b-add1-e77f-5c6f-f6247d2a65d0.png "btn_login_base.png")


先ほど作成したpublicディレクトリに、上記イメージを **login_btn.png** として保存します。下記のパスになっていることを確認してください。

```
public/login_btn.png
```

次にログイン画面を作成します。 **index.ejs** としてテンプレートファイルを下記の内容で作成します。

```html:/index.ejs
<html>
<body>
    <a href="/auth"><img src="/login_btn.png"></img></a>
</body>
</html>
```

*このテンプレートファイルはこのあとインストールするejsというテンプレートエンジンで出力できるファイルですが、今回の中身は純粋なHTMLです。*

次にログイン画面を出力するためのテンプレートエンジンをインストールします。

```
$ npm install --save ejs
```

そして index.jsファイルを編集して、ログイン画面を出力するための設定を追加します。

```javascript:/index.js
// ファイルの末尾に追加
app.use(express.static(__dirname + "/public"));
app.set("view engine", "ejs");
app.get("/", (req, res) => {
    res.render(__dirname + "/index");
})
```

これでサーバーを再起動して http://localhost:5000 にアクセスするとログインボタンが表示されるページが出力されるはずです。
![スクリーンショット 2018-03-14 15.16.01.png](https://qiita-image-store.s3.amazonaws.com/0/26079/12fe7d2d-f2c9-c381-7c8b-586ee6ba443c.png "スクリーンショット 2018-03-14 15.16.01.png")

このボタンをクリックすると、認証フローが開始されます。

## ログインと同時にBotを友だちに追加する

現在LoginのChannelには1つのBot（厳密にはMessaging APIのChannel）を紐づけることができます。この設定をおこなうことで、ユーザーが承認画面で同意するタイミングで自社のBotをユーザーの友だちに追加することができます。Botはユーザーからメッセージに反応できる他、ユーザーに能動的にメッセージを送ることもできるため、従来Webサービスだけでは実現できなかった通知機能を実装することができます。

設定をおこなうには開発者コンソールにアクセスし、まずMessaging APIのChannelを作成する必要があります。この手順について詳しくは下記を参照ください。

[LINEのBot開発 超入門（前編） ゼロから応答ができるまで - チャネルを作成する](https://qiita.com/nkjm/items/38808bbc97d6927837cd#channel%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B)


次に該当のLoginチャネルを選択し、「このチャネルにリンクされたボット」にMessaging APIのChannelを紐付けます。
![スクリーンショット_2018-03-08_15_34_00.png](https://qiita-image-store.s3.amazonaws.com/0/26079/06389ee0-18db-936c-d543-04902279e780.png "スクリーンショット_2018-03-08_15_34_00.png")

あとスクリプトを少し修正し、promptオプションを追加しその値に"consent"を設定します。

```javascript:index.js
const login = new line_login({
    channel_id: process.env.LINE_LOGIN_CHANNEL_ID,
    channel_secret: process.env.LINE_LOGIN_CHANNEL_SECRET,
    callback_url: process.env.LINE_LOGIN_CALLBACK_URL,
    prompt: "consent" // 追加
});
```

このオプションは、ユーザーが権限について一度承認していても、再度承認画面を表示するために設定します。これでサーバーを再起動し、ブラウザで再度 http://localhost:5000 にアクセスしてみてください。ログインの後に承認画面が表示され、友だち追加のチェックボックスが表示されるはずです。

![スクリーンショット_2018-03-08_15_51_38.png](https://qiita-image-store.s3.amazonaws.com/0/26079/3d116338-6eec-6a11-adbe-3c06d3068041.png "スクリーンショット_2018-03-08_15_51_38.png")

ここでユーザーが友だち追加にチェックして同意するとBotが友だちに追加されます。ただ、デフォルトではチェックははずれた状態になっており、ユーザーがこのチェックボックスに気づかず先に進んでしまう可能性があります。

この友だち追加をより明確にユーザーに求めるのであれば、さらにスクリプトを修正してbot_promptオプションを追加してその値に"aggressive"と設定します。

```javascript:index.js
const login = new line_login({
    channel_id: process.env.LINE_LOGIN_CHANNEL_ID,
    channel_secret: process.env.LINE_LOGIN_CHANNEL_SECRET,
    callback_url: process.env.LINE_LOGIN_CALLBACK_URL,
    prompt: "consent",
    bot_prompt: "aggressive" // 追加
});
```

これでもう一度サーバーを再起動してブラウザで承認画面に向かうと、同意した後に友だち追加を求めるためのスクリーンが表示されます。
![スクリーンショット 2018-03-08 16.00.09.png](https://qiita-image-store.s3.amazonaws.com/0/26079/761e0132-b7b6-1e53-e7b3-2cbd8c9d01fd.png "スクリーンショット 2018-03-08 16.00.09.png")

ここでユーザーが友だち追加ボタンをクリックしてくれれば、Botが友だちに追加された上でWebサービスにリダイレクトされるはずです。

## トークンを使ったAPIへのアクセス

最後にアクセストークンを使ってAPIへのアクセスを試してみます。私たちはLogin ChannelにBotを紐付けましたが、たとえユーザーがログインの際には友だち追加してくれたとしても、その後ブロックされる可能性もあります。そこで、とあるタイミングで任意のユーザーとBotが友だちになっているかどうか確認するAPIを利用します。

今回は認証後にそのままアクセストークンを使って友だち状況を確認するという流れでコードを修正します。

```javascript:index.js
app.use("/callback", login.callback(
    (req, res, next, token_response) => {
	    // 認証フロー成功時
	    login.get_friendship_status(token_response.access_token).then((response) => {
	        res.json(response);
	    })
	},
	(req, res, next, error) => {
    	// 認証フロー失敗時
    	res.status(400).json(error);
	}
));
```

認証が成功した後、login.get_friendship_status(アクセストークン）を実行して友だち状況を確認しています。成功すれば下記のJSONレスポンスがブラウザに結果が表示されるはずです。

```json:ブラウザへの出力
{"friendFlag": true} //友だちになっていなければfalse
```


# さらに学ぶには

ログインのAPIに関する網羅的なドキュメントは下記の公式サイトにあります。

https://developers.line.me/ja/docs/line-login/overview/

また、今回はline-loginといったミニマムなSDKを利用しましたが、LINE LoginはOpenID Connectの標準仕様に従っているため、OpenID Connectに対応する [passport.js](http://www.passportjs.org/) などの汎用的なライブラリもすべからく利用できます。そのあたりも是非試してみてください。