OpneIDConnect を LINEログインで理解＆実践する！

#　はじめに
本投稿は下記ような方の参考になればと思います。

- 　WEBアプリにLINEログインを組み込んでみたい方
- 　OpenIDConnectについてざっくりと理解したい方
- 　Vueアプリに認証機能を追加したい方
- 　その他WEBアプリの認証・認可等に興味のある方

Vueプロジェクトの作成から、クラウドへのデプロイまでの一連の流れの中で OpenIDConnect を利用したLINEログインの組み込みを紹介していきます。

基本的には公式のドキュメントベースで実装を進めていきます。
https://developers.line.biz/ja/docs/line-login/integrate-line-login/#login-flow
#開発環境
ローカル：
mac（たぶんWindowsでも同じことができるハズ。。。）
VScode
node.jsとかnpmとか導入済み

クラウド：
Azureアカウント（Azure SataticWeb Appsを使います。）
GitHubアカウント（Azure SataticWeb AppsへのデプロイはGitHub経由で行います。）
LINEアカウント（LINE Developersにログインできるようにしておいてください。）


#本投稿で紹介する内容

- LINEログインの認可要求を行う
- LINEログインのアクセストークン、IDトークンを取得する
- Vue + Azure Static Web Appsで簡単なアプリを作る

下記のようなイメージで認可要求やトークン取得を行う流れを紹介したいと思います。
![スクリーンショット 2021-12-18 22.18.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489752/e3e2aa40-3382-fa59-f54b-1360be84f8dc.png)

①フロントエンドで認可要求を行う。
②LINEログイン認証がOKだったら、認可コードを通知。
③認可コードをバックエンドに送る。
④バックエンドが受け取った認可コードを元にアクセストークンとIDトークンを発行を依頼。
⑤バックエンドがトークンを受け取る。
⑥トークンをフロントエンドに渡す。

おそらく、これだけできるようになれば OpenIDConnectの認可フローの概要は理解できると思います。

では、さっそくクライアント側から作っていきます！

#　Vueアプリの作成
Vue CLI を使ってVueアプリを構築していきます。
npmとかVue関係の環境構築の説明は割愛します。

##　VueCLI　のインストール
まずは下記コマンドで Vue CLI をインストール。

```
npm install -g @vue/cli
```

## プロジェクトの作成
　linelogin_vue という名前でプロジェクトを作ります。

```
vue create 　linelogin_vue
```

コマンドを実行すると色々聞いてきます。今回はVueRouterを利用したいので、Manually select featuresを選択します。VueRouterを利用する目的はOpenIDConnectでは認可コードの受け取りなどにブラウザのリダイレクト機能を使用するためです。

コマンド実行後は下記のように選択します。
![スクリーンショット 2021-12-12 17.14.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489752/7f7b8003-c749-671b-362d-519d6bcf0448.png)

試しにVScodeで今回作ったプロジェクトを開き、ターミナルで

```
npm run serve
```

を実行するとVueのデフォルト画面が見えるようになっているはず。

![スクリーンショット 2021-12-12 17.02.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489752/5e76892c-6915-2b55-5b08-aa97d305ec1c.png)

## Vuetifyのインストール
今回は見栄えとかは気にしませんが、あると便利なので入れておきます。
Vuetifyの詳しい説明は割愛しますが、UIとかを良い感じにしてくれるライブラリです。

```
vue add vuetify
```

コマンドを実行するとプリセットを聞かれますがデフォルトでOKです。

コマンドが完了すると、npm run serve で見える画面が下記のようになります。
![スクリーンショット 2021-12-12 17.23.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489752/3ea45844-f92f-e631-5c22-cf839fabc733.png)

## axiosのインストールと設定
APIはaxiosを使いたいので次のコマンドを実行します。

```
npm install --save axios vue-axios
```

そしてmain.jsを編集します。

``` javascript:main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import vuetify from './plugins/vuetify'
import axios from 'axios'
import VueAxios from 'vue-axios'

Vue.config.productionTip = false

Vue.use(VueAxios, axios)

new Vue({
  router,
  vuetify,
  render: h => h(App)
}).$mount('#app')

```


## メインレイアウトの設定

下記のように App.vue を書き換えて今時のWEBアプリっぽくしてみましょう。

```Vue:App.vue

<template>
  <v-app>
    <v-app-bar color="primary" dark app>
      <v-toolbar-title>Vuetify</v-toolbar-title>
      <v-spacer></v-spacer>
      <v-toolbar-items>
        <v-btn text to="/">HOME</v-btn>
        <v-btn text to="/about">ABOUT</v-btn>
        <v-btn text to="/login">LOGIN</v-btn>
      </v-toolbar-items>
    </v-app-bar>
    <v-main>
      <router-view />
    </v-main>
    <v-footer color="primary" dark app>
      Vue LINEログイン
    </v-footer>
  </v-app>
</template>

<script>

export default {
  name: 'App',

  data: () => ({
    //
  }),
};
</script>
```

Vueプロジェクト作成時にVue Routerも導入済みなので、上記のように記載すると

```
 <router-view /> 
```

の部分に別途設定済みの画面を表示することができます。Vue Routerの説明なども割愛します。

この状態でアプリを開いてみると、次のような画面になっているはずです。
![スクリーンショット 2021-12-12 17.45.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489752/1422c52e-733e-8ded-6272-47163dc27113.png)

右上のボタンを押すとプロジェクト作成時に作られたデフォルトのHome.vue、About.vueがそれぞれ表示されます。まだログイン画面(Login.vueというファイル)は作っていないので、LOGINボタンを押してもなにも表示されません。

## LINEログイン画面の作成

LINEログインを行うための画面を作成します。まずは、https://developers.line.biz/ja/docs/line-login/login-button/ からログインボタンのイメージをダウンロードします。

ダウンロードした、

- btn_login_base.png
- btn_login_hover.png

ファイルを ~/src/assets にコピーします。

次に ~/src/views に Login.vue というファイルを作ります。


``` Vue:Login.vue

<template>
  <div class="login">
    <v-container>
      <v-row class="text-center">
        <v-col cols="12">
          <v-hover>
            <v-img
              slot-scope="{ hover }"
              v-if="hover"
              :src="require('../assets/btn_login_hover.png')"
              width="200px"
            >
            </v-img>
            <v-img
              v-else
              :src="require('../assets/btn_login_base.png')"
              width="200px"
              height="auto"
            >
            </v-img>
          </v-hover>
        </v-col>
      </v-row>
    </v-container>
  </div>
</template>

```

## Vue Routerの設定
~/src/router/index.js　を次のように編集します。

``` Javascript:index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
  ,
  {
    path: '/login',
    name: 'Login',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/Login.vue')
  },
  {
    path: '/linelogincallback',
    name: 'Linelogincallback',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/Linelogincallback.vue')
  }

]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

```

この設定を追加する事で、 http://localhost:8080/login にアクセスすると先に追加したログイン画面を表示することができます。右上のログインボタンも押せるようになっているハズです。

![スクリーンショット 2021-12-15 11.03.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489752/03064030-1100-1840-5f89-07dafd0ff68e.png)

また、後々追加するLINEログインコールバック画面に http://localhost:8080/linelogincallback でアクセスできるようになります。

#　LINE　ログインの設定
クライアントアプリの実装は一旦ここまでにして、次はLINEログイン側の設定をしていきます。

##　LINEログインのチャネルの設定
https://developers.line.biz/ja/docs/line-login/integrate-line-login/#create-a-channel

を参考にチャネルを作成してください。

この時、コールバックURLは、
https://localhost:8080/linelogincallback
とします。

後の手順でAzrue Static Web Appsのリソースを作成した後に正規のドメインに修正します。

また、今回はメールアドレスの取得申請はしないこととします。

##　LINEログインの要求
[ユーザーに認証と認可を要求する](https://developers.line.biz/ja/docs/line-login/integrate-line-login/#making-an-authorization-request)を参考にVueアプリのログイン画面を編集します。

``` Vue:Login.vue

<template>
  <div class="login">
    <v-container>
      <v-row class="text-center">
        <v-col cols="12">
          <v-hover>
            <v-img
              slot-scope="{hover}"
              v-if="hover"
              :src="require('../assets/btn_login_hover.png')"
              width="200px"
              v-on:click="login"
            >
            </v-img>
            <v-img
              v-else
              :src="require('../assets/btn_login_base.png')"
              width="200px"
              height="auto"
            >
            </v-img>
          </v-hover>
        </v-col>
      </v-row>
    </v-container>
  </div>
</template>

<script>

  export default {
    name: 'Login',

    methods: {

      login: function(){
        let response_type = 'code';
        let client_id = '*********'; //取得したチャネルID
        let redirect_uri = '*********'; //設定したコールバック関数をURLエンコードしたもの
        let state = '12345abcde';//一旦ここはテキトー
        let scope = 'profile%20openid';
        let nonce = '09876xyz';//一旦ここはテキトー

        window.location.href = 'https://access.line.me/oauth2/v2.1/authorize?response_type=' + response_type + 
                                '&client_id=' + client_id + 
                                '&redirect_uri=' + redirect_uri + 
                                '&state=' + state + 
                                '&scope=' + scope + 
                                '&nonce=' + nonce;
      },
    },
  }

</script>

```

細かい確認はデプロイした後になりますが、LINEログインボタンを押下するとLINEログインのユーザー認証画面にリダイレクトする仕組みです。LINEログインのユーザー認証がOKだったら、次に作成するLINEログインコールバック画面にさらにリダイレクトされる仕組みです。

##　LINEログインコールバック画面を作成する
~/src/views に Linelogincallback.vue というファイルを作ります。

``` Linelogincallback.vue

<template>
  <div class="linelogincallback">
    <v-container>
      <v-row class="text-center">
        <v-col cols="12">
          <p>{{ this.code }}</P>
        </v-col>
        <v-col cols="12">
          <v-btn @click="getToken">トークン発行</v-btn>
        </v-col>
        <v-col cols="12">
          <p>{{ this.idtoken }}</P>
          <p>{{ this.accesstoken }}</P>
        </v-col>
      </v-row>
    </v-container>
  </div>
</template>

<script>

  export default {
    name: 'Linelogincallback',

    data(){
      return{
          code: null,
          idtoken: null,
          accesstoken: null,
      }
    },

    created : function(){
      this.code = this.$route.query.code;
    },

    methods: {

        getToken: function() {
          let url = '/api/token';
          let data = {
            'code': this.code
          }

          this.axios.post(url, data)
            .then(function (response) {
              this.accesstoken = response['access_token'];
              this.idtoken = response['id_token'];
              console.log(response);
            })
            .catch(function (error) {
              console.log(error);
            });
        },
    },
  }

</script>

```

こちらも細かい確認はデプロイした後になりますが、LINEログインのユーザー認証がOKだったらこの画面にリダイレクトされます。リダイレクト時にトークン発行用の認可コードがURLパラメータとして渡されるので、Vueアプリ側でそれを受け取り画面に表示させています。

また、トークン発行ボタンを押下することでバックエンド側に認可コードを渡し、バックエンド側でトークン発行をさせるような仕組みにします。

#　バックエンドアプリの作成＆Azure Static Web Appsの準備
フロントエンド側の準備はできたので次はバックエンド側です。
Azure Static Web Apps を利用してバックエンド側のアプリを作ります。

##　Azure Static Web AppsとAPI利用について
https://docs.microsoft.com/ja-jp/azure/static-web-apps/add-api?tabs=vue

まずは、このサイトを一読して必要な上記で作ったVueアプリに必要なライブラリのインストール等を行ってください。

##　LINEログインのトークン発行をするAPIを作成する
上記のドキュメントの[APIの作成](https://docs.microsoft.com/ja-jp/azure/static-web-apps/add-api?tabs=vue#create-the-api)のセクションを参考に新しいAPIを作成します。

関数名はtokenとします。

自動生成された、index.jsを下記のように変更します。

``` javascript:index.js
const axios = require('axios');

module.exports = function (context, req) {

    let url = 'https://api.line.me/oauth2/v2.1/token';

    let config = {
                    headers: {
                      'Content-Type': 'application/x-www-form-urlencoded',
                    }
                  };

    let grant_type = 'authorization_code';
    let code = context.req.body['code'];
    let redirect_uri ='*********'; //LINE Developersコンソールで設定したコールバック関数をURLエンコードしたもの
    let client_id ='*********'; //LINE Developersコンソール取得したチャネルID
    let client_secret ='*********'; //LINE Developersコンソール取得したチャネルシークレット

    var params = new URLSearchParams();
    params.append('grant_type',grant_type);
    params.append('code',code);
    params.append('redirect_uri',redirect_uri);
    params.append('client_id',client_id);
    params.append('client_secret',client_secret);
    
    axios.post(url, params, config)
    .then(function (response) {
        context.res.json(response.data);
    })
    .catch(function (error) {
        context.res.json(error);
    });
};
``` 

こちらも細かい確認はデプロイした後になりますが、先に作成した Linelogincallback.vue でトークン発行ボタンを押すと、{'code': [LINEログインから受け取った認可コード]}というJSONを /api/token　にPOSTします。するとこちらのプログラムでそのJSONを受け取り、あらかじめ設定していたLINEログインのチャネル情報等と合わせてLINEログインのAPIに対してトークン発行を行うようなイメージです。

これで基本的なプログラムの準備は完了です。


##　GitHubリポジトリの準備
GitHubに任意のリポジトリを作成します。

ただし、Login.vue 及び index.js（apiのコード）でLINEログインのコールバック関数の設定を修正する必要があるので、この時点ではまだ作ったコードをプッシュする必要はありません。

##　Azure Static Web Appsの作成とGitHubの連携
AzureにログインしてAzure Static Web Appsの新しいリソースを作ります。

![スクリーンショット 2021-12-19 15.17.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489752/2c90ba1d-1e81-5fc1-be1b-284297c92dbd.png)

Market　Place　で"Static Web Apps"と検索すると"静的アプリ"というリソースが出てくるので、作成ボタンを押下します。

リソースのサブスクリプション、リソースグループ、名前、プラン、リージョンは適宜設定してください。
デプロイの詳細では GitHub　を選択してください。その後は画面に従って、GitHubアカウントの認証を行い、上記で作成したリポジトリを連携させてください。

![スクリーンショット 2021-12-19 15.32.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489752/0ecc3a74-f99f-6ff2-6a77-191d3829bbd9.png)

上記はAzure Static Web Appsの名前を"linelogin_vue"とした時のリソース画面です。
右上のURLがデプロイした時のアクセス先です。

##　コールバック関数の修正
ここでデプロイ先のドメイン名が確定したので、先に http://localhost:8080/linelogincallback として設定した下記の部分を、https://[Azure Static Web Appsのドメイン]/linelogincallback に変更してください。

- LINE　Developersコンソールのコールバック関数設定
- Login.vue
- index.js（作成したapiのコード）
　

##　Azure Static Web Apps へのデプロイ
上記でAzure Static Web Appsに連携させたGitHubリポジトリにプッシュすれば、自動的にサーバにデプロイされます。

#　画面動作確認
準備したAzure Static Web AppsのURLにアクセスします。

![スクリーンショット 2021-12-18 21.42.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489752/a0d5cd86-2e18-92f2-de7b-9397256ee025.png)

こんな感じで見えるはず。

次にログイン画面にアクセスします。

![スクリーンショット 2021-12-18 21.43.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489752/1e5b3164-6be6-0faa-eb59-cd1afe57ec0c.png)

ここでログインボタンを押すとLINEのユーザー認証画面に遷移します。
実際のユーザー情報の部分は黒塗りしてあります。

![スクリーンショット 2021-12-18 21.44.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489752/48a90b07-d705-450a-ab14-1831ce7b076e.png)

ここでユーザー認証が成功するとコールバック画面に遷移して、受け取った認可コードを表示します。
![スクリーンショット 2021-12-18 21.47.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489752/104cdefd-d315-8306-dab5-1764e760f9e4.png)

さらにトークン発行ボタンを押下すると、バックエンド側に認可コードを渡してアクセストークンとIDトークンを発行します。
トークンはそれぞれ一部黒塗りしています。
![スクリーンショット 2021-12-18 21.50.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489752/71c4e7f4-f864-a486-ee70-dc79016d4560.png)

ここまでくればあとは受け取ったトークンを使ったり、バックエンド側のAPI機能を拡充して自由に認証機能を持ったWEBアプリを作っていけると思います！

#さいごに
LINEアカウントは今やスマホを持っている人ほぼ全員が持っていると言っても過言ではないと思います。個人的にはBtoCのWEBアプリやサービスではもっと広がってくれると良いなーと思います。自分もそういったものを作る際は導入を検討していきます。


#さいごのさいごに
あと、読んでいただいた方に一つ謝らなければいけない事があります。実は今回の紹介の通りに実装しても下記イメージ図でいう、④と⑤の部分がうまく動きません。。。。。。

![スクリーンショット 2021-12-18 22.18.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489752/e3e2aa40-3382-fa59-f54b-1360be84f8dc.png)

バックエンド側でaxiosを使ってLINEログインのAPIにアクセスしようとしているのですが、おそらく 'Content-Type': 'application/x-www-form-urlencoded' でのPOSTがうまくできず、HTTP400のエラーが発生してしまいます。

そのため、実は画面動作確認の中で紹介している、アクセストークンとIDトークンの画像はバックエンド側で取得したものではなく、上記図でいう②で取得した認可トークンを元にPCの RESTクライアントアプリで取得したものとなります。

以前、ASP.NETで同様のことを試した時はうまくいったのですが、node.jsだと全然うまくいかず。

本当にここまで紹介しておいて申し訳ないのですが、もしaxiosで'Content-Type': 'application/x-www-form-urlencoded'でのPOSTについて詳しい方いれば、コメント等でご教示いただけると幸いです。各種ドキュメントを見ていても今回のような実装でもいいような気がするのですが。。。。。。。。

もし、需要があればほかのフレームワークで試してみた記事も書いてみようと思います！！