Nuxt Basic AuthによるBadRequest(400)の解消

# はじめに

関係者で保有するデータを見える化するため、Nuxt.jsを利用したプロトタイプ開発を実施していました。
コンテンツ開放前は、Basic認証をかけて関係者のみに展開できるようにするため、
Nuxt.jsに簡単にBasic認証をかけられる「[nuxt-basic-auth-module](https://www.npmjs.com/package/nuxt-basic-auth-module)」を利用したのですが、
諸所の条件が重なり、データ表示の際にBad Request(400)が発生するようになってしまいました。

解決することができたので、それらの過程をまとめてみました。


# 実現しようとしていたこと

当初は下図のイメージで、プロトタイプを構築しようとしていました。
データはDBは利用せず、S3に保存し、API Gateway経由で取得するようにしています。
S3は公開領域に保存せず、API Gatewayのキー情報をヘッダーに入れて取得するようにしています。

![初期インフラ構成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498107/ab025d37-ad16-5b81-ec31-e48467ac4d0e.png)



# nuxt-basic-auth-moduleの導入

Nuxt.jsアプリケーションやAPI Gateway経由でS3のファイルでアクセスする処理は、下記のような感じで出来上がったため、早速 nuxt-basic-auth-moduleを導入することとしました。

```javascript
const response = await $axios.$get($config.API_GATEWAY_URL) {
  headers: {"x-api-key": $config.X_API_KEY}
}
return {
  data: response.data,
}
```


nuxt-basic-auth-moduleは下記の記事を参考に導入しました。

- [Nuxt.js アプリケーションに簡単にベーシック認証を付与できる nuxt-basic-auth-module のご紹介](https://qiita.com/potato4d/items/e37d9acf320d7c1b327c)

あっさりと導入できたので、お客様に展開するため、アプリケーションのデプロイを実施しました。


# ヘルスチェックのエラー対応

いつまでたっても反映されないので、ECSのサービスのログを見てみると下記のようにコンテナ起動・削除が繰り返されていました。

```
service xxxxxx-service has stopped 1 running tasks: task e68b4001fa904aa187ac793bdd48810a.
080ad5cf-d721-4ab3-b8c7-d3b048f9504f
2021-12-29 17:57:29 +0900
service xxxxxx-service deregistered 1 targets in target-group TG-ecs-xxxxxx
e7892e21-5e35-45c0-8130-7bd0c6ea89d5
2021-12-29 17:57:29 +0900
service xxxxxx-service (port 3000) is unhealthy in target-group TG-ecs-xxxxxx due to (reason Health checks failed with these codes: [401]).
00a9e933-e8d0-4f41-834c-c0894b2eb0a5
2021-12-29 17:52:25 +0900
service xxxxxx-service registered 1 targets in target-group TG-ecs-xxxxxx
c1367a52-62b8-419e-bc13-b76b51464058
2021-12-29 17:51:19 +0900
service xxxxxx-service has started 1 tasks: task e68b4001fa904aa187ac793bdd48810a.
e7ee4c31-86b3-47c4-89a8-f1b8e38fd5c7
2021-12-29 17:51:08 +0900

（省略）

service xxxxxx-service has stopped 1 running tasks: task cd0081685db84991ac02e88dd85269db.
f6f36466-6821-471b-acfa-a2e4c2e13d53
2021-12-29 17:45:07 +0900
service xxxxxx-service deregistered 1 targets in target-group TG-ecs-xxxxxx
53e05ed0-fe70-4618-80e6-8581dc0327ac
2021-12-29 17:45:07 +0900
service xxxxxx-service (port 3000) is unhealthy in target-group TG-ecs-xxxxxx due to (reason Health checks failed with these codes: [401]).
9ffacb22-29ad-4440-84c7-8b8256017fc9
2021-12-29 17:40:10 +0900
service xxxxxx-service registered 1 targets in target-group TG-ecs-xxxxxx
e572bc65-a3ee-42a6-8e3d-417848e3a668
2021-12-29 17:39:13 +0900
service xxxxxx-service has started 1 tasks: task cd0081685db84991ac02e88dd85269db.
```

原因はヘルスチェックで指定していたパス「/」に対して、Basic認証が動くようになってしまったため、ヘルスチェックが失敗してしまい、コンテナ起動・削除が繰り返されていたのが原因でした。

そこで「healthcheck.vue」というファイルを「pages」ディレクトリに保存し、「/healthcheck」でヘルスチェックを行うように設定することにしました。

# 特定のパスを Basic認証から除外したい

ただ該当のパスをBasic認証から除外する必要があり、「nuxt-basic-auth-module」でどのようにしたらよいか？を調べることとなりました。

- [nuxt-basic-auth-module](https://www.npmjs.com/package/nuxt-basic-auth-module)

サンプルには記載されていなかったのですが、「match」という設定項目があり、
下記の通り記載されています。

```
match
type: String(regex literal) | Function
required: false
The target path. This allows you to set up basic authentication that is limited to routes that match regular expression literals or where the function returns true.
```

なるほど正規表現で指定すればいいんですね。
ということで「/healthcheck」を除外（否定）する正規表現を記載して設定してみたのですが、
なかなかうまく動きません。（おそらく正規表現の記載方法が間違っているのかなとは思います。）

もう１つ「function」で指定できるとのことでしたので、以下の functionを記載して、設定してみました。

```javascript
  // Basic Authentication
  basic: {
    name: 'xxxxxxxx',
    pass: 'xxxxxxxx',
    enabled: 'true',
    match: req => {
      return req.url !== '/healthcheck' 
      && req.url.indexOf('_nuxt') == -1
      && req.url.indexOf('_loading') == -1
    }
  },

```

※_nuxtや_loadingも結果として必要で、入れないとBasic認証に引っかかって、アプリケーションが動作しませんでした。

これでヘルスチェックも成功するようになり、無事デプロイ完了しました。

後は動作確認をしたら、お客様に展開するだけです。


# ファイルサイズが大きすぎた（S3 Signed URLの導入）

アプリケーションを表示しようとしたところ、データ取得のAPIで「500エラー（Internal server error）」
調べていたら、Lambdaではレスポンスのサイズ制限があるとのことでした。

下記の記事を参考に、S3のPre-Signed URLを導入し、解決を図ることにしました。

- [Lambdaで6MBを超えるデータをReturnできなかったので、S3のPre-Signed URLを使った話](https://dev.classmethod.jp/articles/lambda-over-6mb-response-use-s3-pre-signed-url/)

■S3のPre-signed URLを利用したアクセス

```javascript
// Pre-signed URLの取得
const url = await $axios.$get($config.API_GATEWAY_URL) ~
  headers: {"x-api-key": $config.X_API_KEY}
})
// S3ファイルの取得
const response = await $axios.$get(url)

return {
  data: response.data,
}
```


# Bad Request (400)の発生と解消

500エラーは解消したのですが、今度は下記のようにBad Request(400)エラーが発生するようになってしまいました。

![BadRequest.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498107/f2c3d5f9-0f66-a4bd-365f-d08646fee934.png)

原因探るのには結構時間がかかってしまったのですが、Basic認証が成功した後のリクエストでは、「Authentication」ヘッダーが自動で設定されていて、S3の署名付きURLでアクセスした場合、エラーとなってしまっているのが原因でした。

- [vuejs2 : Axios -1コールのみでヘッダーの許可を削除します](https://www.fixes.pub/program/569507.html)


そのためS3の署名付きURLの呼び出し前で「Authentication」ヘッダーを削除して、アクセスするようにしたところ、無事アクセスができるようになりました。

```javascript
// Pre-signed URLの取得
const url = await $axios.$get($config.API_GATEWAY_URL) ~
  headers: {"x-api-key": $config.X_API_KEY}
})
// Authenticationヘッダーの削除
delete $axios.defaults.headers.common.authorization
// S3ファイルの取得
const response = await $axios.$get(url)

return {
  data: response.data,
}
```

ようやくお客様にプロトタイプを展開できそうです。

でも今後フィードバックが返ってくるので、これからが本番です。