Lambdaの関数 URL(Function URLs) を使用時、Basic認証をかける

# はじめに
先日、Lambda単体でHTTPSエンドポイントから実行可能になる機能が追加しました。
特定のリソースのみLambdaを実行できるようにするためには、Basic認証を適用する必要があったため、その方法についてまとめます。

https://dev.classmethod.jp/articles/aws-lambda-function-urls-built-in-https-endpoints/

# Lambdaを実行する認証タイプ

- `AWS_IAM` – IAM の権限に基づいて、リクエストを認証および承認します。権限は、`lambda:InvokeFunctionUrl`を付与したIAMポリシーをIAMロール、もしくは、IAMユーザーに適用し、lambdaを呼びます。
    - アクセスするためには`AWS Signature Version 4 (SigV4)`を使用して、HTTPリクエストに対して署名する必要があります。

- `NONE` – Lambda を呼び出す前の認証を実行しません。関数 URL への、未認証でパブリックなアクセスを許可するには、このオプションを選択します。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/urls-auth.html

特定のリソースのみLambdaを実行できるようにするためには、認証タイプの`AWS_IAM`を選択し、権限を付与したawsリソースもしくは、AWS SDKを使用する方法(下記記事)があります。

https://zenn.dev/umatoma/articles/d04d8417fa2e70

![スクリーンショット 2022-07-03 18.35.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/43906d01-5c1d-76fb-21cb-79e6fd954b69.png)

ただし、`AWS_IAM`が使用できず、`NONE`にした場合でも特定のリソースのみLambdaを使用できるようにBasic認証をかけていきます。

# Lambda作成
- 作成時、関数URLを有効にする。
![スクリーンショット 2022-07-03 18.37.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/988747/3430d3ac-465b-c3a5-1ca7-deadf28ed91d.png)
- ユーザー名とパスワードは環境変数にします。
    - PASSWORD
        - password
    - USERNAME
        - admin

## Lambdaのコード
```js
exports.handler =  async (event) => {
  console.log('event:', JSON.stringify(event, null, 2));
  const authorizationHeader = event.headers.authorization;

  if (!authorizationHeader) return ('Unauthorized');
  const encodedCreds = authorizationHeader.split(" ")[1];
  const plainCreds = (new Buffer.from(encodedCreds, 'base64')).toString().split(':');
  const username = plainCreds[0];
  const password = plainCreds[1];

  if (!(username === process.env.USERNAME && password === process.env.PASSWORD)) return ('Unauthorized');
  
  // Write the code you want to execute
  return ( "ok");
};
```
今回、誰でもURLを呼べはしますが、Basic認証に成功しなければ、以降のコードを実行できません。

event内容はこちらです。
```json
{
    "version": "2.0",
    "routeKey": "$default",
    "rawPath": "/",
    "rawQueryString": "",
    "headers": {
        "authorization": "Basic YWRtaW46cGFzc3dvcmQ=",
        "x-amzn-tls-cipher-suite": "ECDHE-RSA-AES128-GCM-SHA256",
        "x-amzn-tls-version": "TLSv1.2",
        "x-amzn-trace-id": "Root=1-62c16503-6b13cbf473d2d54659fd3bd1",
        "x-forwarded-proto": "https",
        "host": "3uu2x5movnumaapubfmjdonp5a0cwvde.lambda-url.ap-northeast-1.on.aws",
        "x-forwarded-port": "443",
        "x-forwarded-for": "111.111.111.111",
        "accept": "*/*",
        "user-agent": "curl/7.79.1"
    },
    "requestContext": {
        "accountId": "anonymous",
        "apiId": "3uu2x5movnumaapubfmjdonp5a0cwvde",
        "domainName": "3uu2x5movnumaapubfmjdonp5a0cwvde.lambda-url.ap-northeast-1.on.aws",
        "domainPrefix": "3uu2x5movnumaapubfmjdonp5a0cwvde",
        "http": {
            "method": "GET",
            "path": "/",
            "protocol": "HTTP/1.1",
            "sourceIp": "111.111.111.111",
            "userAgent": "curl/7.79.1"
        },
        "requestId": "6492f838-fabb-4fbe-a474-31f44bf7d85f",
        "routeKey": "$default",
        "stage": "$default",
        "time": "03/Jul/2022:09:44:35 +0000",
        "timeEpoch": 1656841475184
    },
    "isBase64Encoded": false
}
```

## Lambdaを呼んで見る
ローカルから呼んでみます。
```terminal
$ curl https://admin:password@3uu2x5movnumaapubfmjdonp5a0cwvde.lambda-url.ap-northeast-1.on.aws/

ok

$ curl https://3uu2x5movnumaapubfmjdonp5a0cwvde.lambda-url.ap-northeast-1.on.aws/\
 -H "authorization:Basic YWRtaW46cGFzc3dvcmQ="

ok
```
どちらの呼び方でもBasic認証を成功しています。
`YWRtaW46cGFzc3dvcmQ=`は、`admin:password`をエンコードしています。

```terminal
$ curl https://3uu2x5movnumaapubfmjdonp5a0cwvde.lambda-url.ap-northeast-1.on.aws/\         
 -H "authorization:Basic YWRtaW46cGF"

Unauthorized
```
失敗した場合、`Unauthorized`と表示されます。

# 参照

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-urls.html