AmazonCognitoでGoogle/Facebook認証してトークンを取得する

# はじめに

普段からWebアプリケーションにログイン機能を実装する際、バックエンドにAmazonCognitoを利用してきました。
Cognitoはログイン認証をサーバレスに実現してくれるマネージドなサービスです。開発者はログインユーザの情報を管理する必要が(ほとんど)ありません。
またAWSのリソースへのアクセス権限を一時的に発行することもできるので、ログイン後のトークンを利用して、アプリケーションからAWSの各リソースを利用することができます。
今回はそんなCognitoの外部IDプロバイダ認証を利用してログインし、トークンを取得するまでの流れをGoogleとFacebookでやっていこうと思います。

## Cognitoの準備

### Cognitoのデプロイ
AmplifyCLIを利用してサクッと作っていきます。フロントはVue.jsを使っています。
[この記事](https://qiita.com/is_ryo/items/b9feeb017366b0cee544)を参考にしていただければ、Cognitoを利用したログイン画面を用意することができると思います。

### その他下準備
その他細かい設定を行っていきます。

#### アプリクライアントの設定
上記の手順でCognitoをデプロイするとアプリクライアントが2つ作成されていると思います。
今回はWebアプリケーションからログインするので、`app_clientWeb`とついている方のクライアントを利用します。
その設定の中の「サインインとサインアウトの URL」に`http://localhost:8080`を入力しておきます。
<img width="977" alt="ユーザープール - Amazon Cognito 2019-02-18 13-04-01.png" src="https://qiita-image-store.s3.amazonaws.com/0/95875/f4fb4e3c-6e7e-7299-e875-4c9fa373ee37.png">

#### ドメインの設定
AmazonCognitoドメインを設定する必要があります。
左メニューから「ドメイン名」を選択して、ドメインのプレフィックスに任意の文字列を入力します。利用できるサブドメインだったら登録されます。
<img width="982" alt="ユーザープール - Amazon Cognito 2019-02-18 13-06-48.png" src="https://qiita-image-store.s3.amazonaws.com/0/95875/34943140-526b-2c16-fe77-c98a0a09ac7d.png">
**※このドメインは各外部IDプロバイダ認証からのリダレクトURIで利用するのでメモっておいてください。**

これでCognitoの下準備は完了です。

## Google認証
Google認証の設定はGoogleCloudPlatform(GCP)で進めていきます。
外部からGoogle認証を利用するために、認証情報の設定します。

① 左メニューバーの「APIとサービス」から「認証情報」を選択する
<img width="472" alt="ホーム – vue-signin-oauth – Google Cloud Platform 2019-02-18 11-43-17.png" src="https://qiita-image-store.s3.amazonaws.com/0/95875/24c3d2c0-023d-5f53-35e5-7fd27d752fd9.png">

② 「OAuth 同意画面」を選択し、必要な情報を入力する
<img width="603" alt="同意画面 - vue-signin-oauth - Google Cloud Platform 2019-02-18 11-45-37.png" src="https://qiita-image-store.s3.amazonaws.com/0/95875/0027f05f-f5e2-103a-137c-12e7e4563e36.png">

③ 「認証情報」を選択し、「OAuthクライアントID」を選択 
※初めて作成する場合は画面が違いますが、同じように「OAuthクライアントID」を選択していただければ、その後は同じ内容です。
<img width="514" alt="認証情報 - vue-signin-oauth - Google Cloud Platform 2019-02-18 11-49-09.png" src="https://qiita-image-store.s3.amazonaws.com/0/95875/a739a2ec-1bf7-20eb-2948-b25137fc8c7d.png">

④ 必要な情報を入力していく
リダイレクトURIは `https://<Cognitoドメイン>.auth.ap-northeast-1.amazoncognito.com/oauth2/idpresponse` になります。
<img width="622" alt="OAuth クライアント ID の作成 - vue-signin-oauth - Google Cloud Platform 2019-02-18 11-57-44.png" src="https://qiita-image-store.s3.amazonaws.com/0/95875/671ce801-12d2-1990-80e9-07239d01ac74.png">

⑤ クライアントIDとシークレットを取得
**認証情報が作成されるとIDとシークレットが表示されるので、後ほど使うのでメモっておいてください。**
<img width="443" alt="認証情報 - vue-signin-oauth - Google Cloud Platform 2019-02-18 13-10-23.png" src="https://qiita-image-store.s3.amazonaws.com/0/95875/ce76cfb0-2fbe-c558-9360-f5770128a996.png">

これでGoogleの設定は完了です。

## Facebook認証
Facebook認証は「facebook for developers」で設定します。

① グローバルメニュー「マイアプリ」から「新しいアプリを追加する」を選択
<img width="454" alt="Facebook for Developers 2019-02-18 13-20-39.png" src="https://qiita-image-store.s3.amazonaws.com/0/95875/c63f3252-60ed-61d6-bfb3-4c6129879564.png">

② 表示名を入力
<img width="608" alt="Facebook for Developers 2019-02-18 13-22-11.png" src="https://qiita-image-store.s3.amazonaws.com/0/95875/ab598547-3311-2a30-82de-605209aa48b5.png">

③ ダッシュボードの「製品の追加」から「Facebookログイン」を選択
<img width="1202" alt="oauth-demo ― ダッシュボード - 開発者向けFacebook 2019-02-18 13-26-42.png" src="https://qiita-image-store.s3.amazonaws.com/0/95875/19d04db3-1bbf-6f64-d740-338911c9aaf0.png">

④ 左メニューの「Facebookログイン」の「設定」から、リダイレクトURIを設定
リダレクトURIはGoogleと同じです。
<img width="1049" alt="oauth-demo ― Facebookログイン - 開発者向けFacebook 2019-02-18 13-32-27.png" src="https://qiita-image-store.s3.amazonaws.com/0/95875/cde83d39-73f5-4fc9-a0d8-d6335001390c.png">

⑤ 左メニューの「設定」の「ベーシック」から「アプリID」と「app sercret」を取得
**こちらも後ほど利用するのでメモっておいてください。**
![vue-signin-oauth ― 設定 - 開発者向けFacebook 2019-02-18 15-34-28.png](https://qiita-image-store.s3.amazonaws.com/0/95875/73e02b79-6287-d939-8b38-f9d5d0faf91c.png)

⑥ アプリを公開する
開発モードのままでは利用できなくなるので、公開する必要があります。
![vue-signin-oauth ― ダッシュボード - 開発者向けFacebook 2019-02-18 15-55-59.png](https://qiita-image-store.s3.amazonaws.com/0/95875/c81de25b-69f2-d708-e977-343dd3a4eb63.png)


これでFacebookの設定は完了です。

## アプリの設定

Webアプリケーションの処理はCognitoが用意してくれている、各外部IDプロバイダ認証用のリンクを呼ぶだけです。
ボタンのClickEventで着火させるといいかもしれませんね。

```js:script
/**
 * CognitoDomain: AmazonCognitoドメイン
 * CognitoAppClientID: Cognitoに紐付いているアプリクライアントのID
 * RedirectURI: リダイレクト先のURI
 */

// Google認証
function OAuthForGoogle() {
  window.location.assign(
    "https://" + 
    CognitoDomain +
    ".auth.ap-northeast-1.amazoncognito.com/oauth2/authorize?response_type=code&client_id=" +
    CognitoAppClientID
    "&redirect_uri=" +
    RedirectURI
    "&identity_provider=Google"
  );
}

// Facebook認証
function OAuthForFacebook() {
  window.location.assign(
    "https://" + 
    CognitoDomain +
    ".auth.ap-northeast-1.amazoncognito.com/oauth2/authorize?response_type=code&client_id=" +
    CognitoAppClientID
    "&redirect_uri=" +
    RedirectURI
    "&identity_provider=Facebook"
  );
}
```

### Google認証時の画面
<img width="492" alt="ログイン - Google アカウント 2019-02-18 13-50-49.png" src="https://qiita-image-store.s3.amazonaws.com/0/95875/4af4c4d1-9d40-db13-c1c3-0314f99e98ed.png">

### Facebook認証時の画面
<img width="508" alt="Facebook 2019-02-18 14-12-15.png" src="https://qiita-image-store.s3.amazonaws.com/0/95875/610731c2-ddf5-8f1e-d388-725b7baba901.png">

## Cognitoトークンの取得
ログインするとURLパラメータに`code`が埋まっています。この`code`を利用してCognitoトークンを取得します。
下記のソースコードはログインしたあとの`code`を利用して、Cognitoトークンとユーザー情報を取得し、Amplifyで使えるようにlocalStorageにセットする際の一例です。

```ts:script
const localStorageMainKey: string = "CognitoIdentityServiceProvider." + CognitoAppClientID;
const code: string = this.$route.query.code as string;
if (code && localStorage.getItem("loginStatus") !== "logined") {
  const params: URLSearchParams = new URLSearchParams();
  params.append("grant_type", "authorization_code");
  params.append("redirect_uri", RedirectURI);
  params.append("code", code);
  params.append("client_id", CognitoAppClientID);

  // Tokenの取得
  axios.post(CognitoBaseURL + "/oauth2/token", params, {
    headers : {
      "Content-Type": "application/x-www-form-urlencoded"
    }
  }).then((token: any) => {
    const bearer: string = "Bearer " + token.data.access_token;
    // ユーザー情報の取得
    axios.post(CognitoBaseURL + "/oauth2/userInfo", {}, {
      headers : {
        "Content-Type": "application/x-www-form-urlencoded",
        "Authorization": bearer
      }
    }).then((userInfo: any) => {
      // Amplifyを利用するためにlocalStorageにセットする
      const localStorageSubKey: string = localStorageMainKey + "." + userInfo.data.sub;
      localStorage.setItem(localStorageSubKey + ".accessToken", token.data.access_token);
      localStorage.setItem(localStorageSubKey + ".idToken", token.data.id_token);
      localStorage.setItem(localStorageSubKey + ".refreshToken", token.data.refresh_token);
      localStorage.setItem(localStorageSubKey + ".clockDrift", "0");
      localStorage.setItem(localStorageMainKey + ".LastAuthUser", userInfo.data.sub);
      localStorage.setItem("loginStatus", "logined");
    });
  }).catch((err: any) => {
    console.error(err);
  });
}
```

## さいごに

これで、GoogleとFacebookのアカウントを使ってログインし、Cognitoからトークンを取得することができました。
ただ前述したとおり、ログインしただけではトークンを返してくれないので、取得しに行かなければいけないのが、少し手間がかかるといった印象でした。
あとはTwitter認証をしたいと思っているので、また記事を更新しようと思っています。
ではまた！！！