LambdaにBasic認証を追加する方法とLINEからAndroidで開けない問題の対応方法

タイトルそのままです。
LambdaにBasic認証を追加する方法とLINEのアプリ内ブラウザからアクセスした際に発生する問題の対処法について記載します。
Basic認証だけならググれば簡単に出てくるのですが、後半の対処に時間がかかったので同じような状況の方の助けになれば幸いです。

# LambdaにBasic認証を追加する

lambdaのコードとしては以下のようになります。シンプルなので説明は簡単なコメントだけにしておきます。
自分の場合はこれをCloudFrontに設定してページにBasic認証をかけるために使用しました。

```js:basic_auth.js
'use strict';

exports.handler = (event, context, callback) => {
    // リクエストヘッダーなど取得
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    // IDとパスワード
    const basicAuthId = 'id' // ID
    const basicAuthPath = 'password' // パスワード
    // Construct the Basic Auth string
    const authString = 'Basic ' + new Buffer(basicAuthId + ':' + basicAuthPath).toString('base64');

    // basic認証
    if (typeof headers.authorization == 'undefined' || headers.authorization[0].value != authString) {
            const body = 'Unauthorized';
            const response = {
                status: '401',
                statusDescription: 'Unauthorized',
                body: body,
                headers: {
                    'www-authenticate': [{key: 'WWW-Authenticate', value:'Basic'}]
                },
            };
            callback(null, response);
    }
    // 通ったら処理を継続
    callback(null, request);
};
```

# LINEのAndroidで発生するポップアップ出ない問題
一見これだけでOKそうに見えるのですが、上記の実装だと、AndroidのLINEで開こうとするとベーシック認証画面を出さずに「Unauthorized」となって終了してしまいます。
どうやらこれは[LINEのアプリ内ブラウザはポップアップをブロックしている](https://developers.worksmobile.com/jp/community/all/364?lang=ja&selectedCategory=)ことが原因のようです。
(iosでは発生しない)

# 対処法
そこでlambda側でandroidの場合はchromeで開くように実装します

```js
exports.handler = (event, context, callback) => {
    // リクエストヘッダーなど取得
    const request = event.Records[0].cf.request;
    const headers = request.headers;
    const userAgent = headers['user-agent'][0].value

    if(userAgent.match("Android") && userAgent.match('LINE')) { // userAgentチェック
        const response = {
            status: '302',
            statusDescription: 'Found',
            headers: {
                location: [{
                    key: 'Location',
                    value: 'googlechrome://navigate?url=https://' + request.uri, // googlechromeで開く
                }],
            },
        };
        callback(null, response);
    }
    ...
}
```

ポイントは2つで
まずこの部分でブラウザがAndroidであることとLINEで開いていることをチェックします。
(このあとchromeで開かせるのでLINEであることをチェックしないと無限ループしてえらいことになります笑)

```js
if(userAgent.match("Android") && userAgent.match('Line')) { ... }
```

2つ目はchromeで開かせる箇所でこのように書くことで、urlschemeで端末にchromeで開かせることができます。
(因みにはじめからLINEに
googlechrome://navigate?url=https://google.co.jp
を貼り付けて開かせればよくね？と思って試したんですが、上記のように上手くパースされずクリックできる青リンクにならなかったです。)

```
googlechrome://navigate?url=https://google.co.jp // chromeでhttps://google.co.jpを開く
```

のように書くことでAndroid端末でchromeで開かせることができます。(chromeがない場合はchromeのダウンロードページに飛びます)

因みに今回は不要ですがiOSの場合はこんな感じです。

```
googlechromes://apple.co.jp
```

これでLINEのブラウザにも対応したBasic認証が完成しました。最終的なコードは以下のようになります。


```js

'use strict';

exports.handler = (event, context, callback) => {
    // リクエストヘッダーなど取得
    const request = event.Records[0].cf.request;
    const headers = request.headers;
    const userAgent = headers['user-agent'][0].value
    const domain = 'awsome.app'
    if(userAgent.match("Android") && userAgent.match('Line')) {
        const response = {
            status: '302',
            statusDescription: 'Found',
            headers: {
                location: [{
                    key: 'Location',
                    value: `googlechrome://navigate?url=https:${domain}//refe.love${request.uri}`, // googlechromeで開く
                }],
            },
        };
        callback(null, response);
    }

    // IDとパスワード
    const basicAuthId = 'id' // ID
    const basicAuthPath = 'password' // パスワード
    // Construct the Basic Auth string
    const authString = 'Basic ' + new Buffer(basicAuthId + ':' + basicAuthPath).toString('base64');


    // basic認証
    if (typeof headers.authorization == 'undefined' || headers.authorization[0].value != authString) {
            const body = 'Unauthorized';
            const response = {
                status: '401',
                statusDescription: 'Unauthorized',
                body: body,
                headers: {
                    'www-authenticate': [{key: 'WWW-Authenticate', value:'Basic'}]
                },
            };
            callback(null, response);
    }
    // 通ったら処理を継続
    callback(null, request);
};
```

LINE以外のブラウザについても同じようなHeaderを送っていればuserAgentのロジック書き換えるだけで対応できるはずです。
おためしあれ :confetti_ball: