Vue-AuthでOAuth2実装する方法

# 概要

Vue-AuthはVue用のライブラリでVueの認証機能実装するのを簡単にさせるように提供されています。

- Vue-Auth：https://github.com/websanova/vue-auth

VueAuthが主にJWT認証に注目していますのでVue-LaravelのJWT認証機能実装するとしたら頭の中でVueAuthという名前がすぐに出てくるでしょう。インターネットで探してみたらいくつかの実装マニュアルも出てきました。

- https://inokawablog.org/code/laravel-vue-jwt-email-auth-2/
- https://scrapbox.io/tommy6073/@websanova%2Fvue-auth

ユーザー・パスワードを入れてloginのAPI経由でJWTトークン取るのは一般的なしかたですがこう言う風に実装したらシステムの数が増えることに従ってアカウント情報も山々覚えなければなりません。一つのアカウントで複数システムにログインできるのは望ましいではないでしょうか？OAuthはその課題に対しての対策ですね。Laravelは既に様々なSNSアカウントでログインできるようにLaravelSociliate提供していますがSPAの場合（特にフロントはVueの場合）Vue-Authで同じことできれば嬉しくなると思います。

弊社はオフショア開発会社ですのでGitlabは重要なツール誰もgitlabアカウント持っているはずです。Vueで開発中の社内システムにログインする際にgitlabアカウントでログインできればその後アカウント管理の作業は劇的に減らし各システムの連携形態になります。そう言うわけでVue-AuthでGitlab経由でログインという機能作ってみました。誰かがすでに同じことしてくれたかとインターネットで探してみましたが出てきた情報は非常に少ないですので自分で作ってみることにしました。手順は皆にご共有したいと思います。

# 事前準備

## Gitlabでの準備

まずはGitlabをOAuthプロバイダとして提供させるようにアプリケーション作成するのは必要です。

- 管理者画面にアクセスして
- 左のサイドメニューからアプリケーション小メニュー選択して
- アプリケーション新規作成ボタンを実行したら下記の画面が出てくるはずです

![スクリーンショット 2020-12-02 16.43.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139068/11596035-87f4-2a3a-2ac2-9ec481a8d10d.png)

- Nameは任意入力項目で有意味の名前ご記入いただけばと思います。
- RedirectURLはフロントのURLになります。ログイン成功したらgitlabからこのURLにリダイレクトされます。例: https://my-vue-app/login/gitlab/callback
- Scopeはこのアプリの情報アクセス範囲という意味でapiとread_userのみ付与されたらいいです。
- Trustedにもチェックしておいてください(チェックしない場合Google認証みたいに"このアプリ信じますか"と質問されます。今回作るアプリは社内システム用のアプリですからチェックしても問題なさそうです）
- サブミット実行すればアプリが作られます。


![スクリーンショット 2020-12-02 16.54.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139068/7c09609d-4c6a-1866-ca27-bde4949e4642.png)

ApplicationIdとSecretはこの後Vue-Authで使われる情報ですのでメモしておいてください。

## フロントの方

事前準備として以下の作業しておいてください
- Vueアプリさくせい
- Vue-Auth導入する

```
yarn install vue-auth
```

OAuthは既存選択される選択肢ではありませんので手動で設定するのは必要です。

まずgitlabログインドライバー作成しましょう

![スクリーンショット 2020-12-02 17.02.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139068/95592a11-670c-153b-517b-9fbb772df058.png)

ご覧の通りプロジェクトのルートフォルダーのしたに`plugins/auth/drivers/oauth2`というフォルダー構成作成しましてgitlab.jsというファイルおいておきます.gitlab.jsの内容は以下になります.

```
export default {
    url: 'https://<gitlab.company.jp>/oauth/authorize',
    params: {
        client_id: '',
        redirect_uri: 'login',
        response_type: 'code',
        scope: '',
        state: {},
    }
}
```
`gitlab.company.jp`は仮値ですので会社のgitlabドメインに適当にご修正ください.

次vue-authライブラリの設定を行います.全ての設定はconfig.jsに入ります。

```
import axios from '@websanova/vue-auth/drivers/http/axios.1.x';
import router from '@websanova/vue-auth/drivers/router/vue-router.2.x';
import oauth2Gitlab  from './drivers/oauth2/gitlab.js';

export default {
    http: axios,
    router: router,
    oauth2: {
        gitlab: oauth2Gitlab,
    },
    tokenDefaultKey: 'token',
    refreshTokenName: 'refresh-token',
    stores: ['storage','cookie'],
    authRedirect: {
        path: '/login'
    },
    notFoundRedirect: {
        path: '/dashboard'
    },
};
```

ご覧の通り、JWT使うようの設定とは比べて少なくなりましたoauth2という設定はキーポイントです。

つきましてログインComponentでログイン機能実装しに行きます.

```Login.vue
                                <CButton
                                        color="light"
                                        variant="outline"
                                        size="lg"
                                        @click="gotoGit()"
                                >
                                    <CIcon name="cib-gitlab"></CIcon>
                                    Gitlab Login
                                </CButton>

-----

        methods: {
            gotoGit() {
                //ログイン中でグルグルアイコン表示しておきます。
                this.$store.dispatch('app/setProcessing', true);

                this.$auth.oauth2('gitlab', {
                    params: {
                        client_id: '....bb8fe2a632c8be219d698c61f06cfe3da93470e295c6',
                        scope: 'read_user',
                    }
                });
            },
        }
```

みたらすぐわかると思います、ここでvue-authのoauth2関数実行することになります。要注意点は二つあります。
- `gitlab`という名前だけはご注意ください。このパラメータはconfig.jsのoauth2で定義しておいた値になります.
- client_idの値は以前アプリで作成した時にメモしたclient_idです.

ここまでいけばボタンクリックしたらgitlabへ移動されgitlabログイン情報入力されてログイン成功したら「https://my-vue-app/login/gitlab/callback」また移動されます。リダイレクトさる時に、URLの後ろにAuthenticationCodeがあります。`login/gitlab/callback`に対してのVueComponentの処理でcode描出できるはずです

```
        mounted() {
            if(this.$route.query.code){
                this.$auth.oauth2( 'gitlab', {
                    url: this.$router.resolve({name:'api.auth.gitlab'}).href,
                    code: true,
                    data: {
                        code: this.$route.query.code
                    },
                    state: this.$route.query.state
                }).then(()=>{
                    this.$store.dispatch('app/setProcessing', false);
                });
            }
        },
``` 

pageへリダイレクトされる際にまずURLにcodeというパラメータがあるかどうか確認してある場合に、code取ってvue-authの関数でバック(Laravel)のAPIを叩いていくの仕組みです。Laravelではcodeを持ってまたgitlabのAPI経由でユーザー情報取得したりログインしたりすることにします.

```api
    Route::group([
        'middleware' => 'api',
        'prefix' => 'auth'
    ], function () {
        Route::post('gitlab', 'AuthController@gitlab');
    });
```

```AuthController.php

    public function gitlab(Request $request)
    {
        //ここはただConfigからgitlabのURL取ります
        $url = Config::get('services.gitlab.url');
        //フロントからもらったauthorization_codeを使ってOAuthの最後のステップ実施してUser情報取ってみる
        $response = Http::post(
            $url . self::TOKEN_PATH, [
            'client_id' => Config::get('services.gitlab.client_id'),
            'client_secret' => Config::get('services.gitlab.client_secret'),
            'code' => $request->code,
            'grant_type' => 'authorization_code',
            'redirect_uri' => Config::get('services.gitlab.redirect_uri'),
        ]);
        //ユーザーとれる場合ログインしてJWT返す
        if ($response->successful()) {
            $result = $response->json();
            if ($result) {
                $response = Http::get(
                    $url . self::USER_PATH,
                    [
                        'access_token' => $result['access_token'],
                    ]
                );
                if ($response->successful()) {
                    /**
                     * Login or create user
                     */
                    $gitlab_user = $response->json();
                    $user = User::where('email', $gitlab_user['email'])->first();
                    if ($user) {
                        $token = $user->createToken(env('PASSPORT_PERSONAL_ACCESS_TOKEN_HRM'), []);
                        return [
                            "token_type" => "Bearer",
                            "expires_in" => $token->token->expires_at->diffInSeconds(Carbon::now()),
                            "refresh_token" => null,
                            'access_token' => $token->accessToken
                        ];
                    } else {
                        $user = new User();
                        $user->email = $gitlab_user['email'];
                        $user->name = $gitlab_user['name'];
                        $user->password = $result['access_token'];
                        $user->save();
                        $token = $user->createToken(env('PASSPORT_PERSONAL_ACCESS_TOKEN_HRM'), []);

                        return [
                            "token_type" => "Bearer",
                            "expires_in" => $token->token->expires_at->diffInSeconds(Carbon::now()),
                            "refresh_token" => null,
                            'access_token' => $token->accessToken
                        ];
                    }
                } else {
                    return $response->throw();
                }
            }
        }
        return $response->throw();
    }
```

ざっとみたら複雑感じられるかもしれませんがアイデアはすごく簡単です.フロントでまずauthorization_codeを取得して取得したauthoziation_codeをその後API経由でバックに渡して、バックはフロントの代わりにauthorization_code使ってgitlabユーザーの情報を取っていきます。正常にとれる場合、通常のログイン実行してJWTトークン生成してフロントへ返す。フロントはVueAuthですので設定の通りにstorateにトーク保存しこれからAPI実行する際にトーク使うことになります

# 終わりに

少し複雑ですが動いていますので共有いたします