外部アプリケーションとの連携で使用する
flowchart LR A["API Geteway"]-->B["トークンベース認証"]-->C["AWS Lambda"]
macOSにインストール
brew install docker brew tap aws/tap brew install aws-sam-cli
sam --version > SAM CLI, version 1.36.0
brew upgrade aws-sam-cli
sam init
✅ TypeScriptのTemplateがあったので、選択する。(これにてTypeScriptの設定は完了)
...省略 AWS quick start application templates: 1 - Hello World Example 2 - Hello World Example TypeScript 3 - Step Functions Sample App (Stock Trader) 4 - Quick Start: From Scratch 5 - Quick Start: Scheduled Events 6 - Quick Start: S3 7 - Quick Start: SNS 8 - Quick Start: SQS 9 - Quick Start: Web Backend
AWSCloudFormationFullAccess IAMFullAccess AWSLambda_FullAccess AmazonAPIGatewayAdministrator AmazonS3FullAccess AmazonEC2ContainerRegistryFullAccess
templateからプロジェクトを作成すると、hello-words
が作成される
cd hello-words && yarn install # 実行しないと sam build で esbuild でエラーになる sam build
Hot Reloadはなかったので、sam build
を再実行する必要がある。
(sam build --beta-features
でyesを選択すると、作成したfuntionに対してesbuildを実行してくれるので、yarn compile
の実行の必要なさそう)
sam build > You can also enable this beta feature with 'sam build --beta-features' yes Running NodejsNpmEsbuildBuilder:CopySource Running NodejsNpmEsbuildBuilder:NpmInstall Running NodejsNpmEsbuildBuilder:EsbuildBundle
sam build --beta-features
sam local start-api > localhost:3000 で起動する
sam local invoke --env-vars env.json
{ "MyFunction1": { "TABLE_NAME": "localtable", "BUCKET_NAME": "testBucket" }, "MyFunction2": { "TABLE_NAME": "localtable", "STAGE": "dev" } }
template.ymlAWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst Globals: Function: Runtime: nodejs10.x Handler: index.handler Timeout: 10 MemorySize: 1024 Environment: Variables: DYNAMO_ENDPOINT: DUMMY_ENDPOINT Resources: A001: Type: AWS::Serverless::Function Properties: CodeUri: ../build/A001/ Events: HelloWorld: Type: Api Properties: Path: /groups Method: GET Environment: Variables: TABLE_GROUP_WORDS: DUMMY_GROUP_WORDS
invoke: 呼び出しの意味
sam build sam local invoke --env-vars env.json sam local start-api
sam local start-api --env-vars env.json
表示されるようになった
const privateKey = process.env.PRIVATE_KEY; console.log('privateKey:', privateKey) > AAAAAAA
template.ymlを変更したら、sam build
ソースコードを変更したら、sam build --beta-features
envを渡したい場合は、--env-vars env.json
をつける。
1.トークンベース(通常Bearerトークンが設定されるヘッダーです。) 2.リクエストパラメータベース
認証情報に複数の値は使用しないので、トークンベース認証で実装してみる。
ローカルではサポートされていないので、デプロイして確認する。 (localhost - SAM Local doesn't appear to be running Authorizer functions - Stack Overflowから)
sam deploy --guided # aws credentialsの設定をしていない場合は、 aws configure
トークン認証用のSECRETを作成する。
openssl rand -hex 32
aws secretsmanager create-secret \ --name 'private-key' \ --secret-binary file://./private-key.pem aws secretsmanager get-secret-value \ --secret-id private-key
aws secretsmanager get-secret-value
--secret-id private-key
Globals: Function: Timeout: 3 Api: Cors: AllowOrigin: "'*'" AllowMethods: "'OPTIONS,POST'" AllowHeaders: "'Content-Type,X-CSRF-TOKEN'" Resources: ExampleFunction: Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction Properties: CodeUri: app/ Handler: app.lambdaHandler Runtime: nodejs14.x Architectures: - x86_64 Environment: Variables: PRIVATE_KEY: PRIVATE_KEY Events: RootEndpoint: Properties: Method: any Path: / Type: Api EverythingElse: Properties: Method: any Path: /{proxy+} Type: Api
responseにも設定してみた
response = { statusCode: 200, body: JSON.stringify({ token: token, }), headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'OPTIONS,POST', }, };
AddDefaultAuthorizerToCorsPreflight: false
と下記の設定を行う。
Api: Cors: AllowOrigin: "'*'" AllowMethods: "'OPTIONS,POST'" AllowHeaders: "'Origin, X-Requested-With, Content-Type, Accept,
responseにもくっつける
const headersOption = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, Authorization', 'Access-Control-Allow-Methods': 'POST,OPTIONS', };
Your template contains a resource with logical ID "ServerlessRestApi", which is a reserved logical ID in AWS SAM. It could result in unexpected behaviors and is not recommended.
botocore.exceptions.NoCredentialsError: Unable to locate credentials
aws configure
でconfigurationを設定する
sam validate --profile=${PROFILE}
Preflight request (プリフライトリクエスト)に関しては、SAMでAPI Gateway認証をする場合は、設定をfalseにするのが良さそう。
Corsについては下記記事がすごく分かりやすかった
なんとなく CORS がわかる...はもう終わりにする。 - Qiita
templateとSecret Managerを使用して、環境別のSecret類を管理するのが楽だと感じた。
sam deploy -t ./template.yaml sam deploy -t template.production.yaml
下記のように設定すると、sam deploy
時にenvを渡すことができる。
Globals: ... Parameters: # 👈 SecretMangerId: Type: String Description: Secret Manager ID Default: staging/my-secret Origin: Type: String Description: Origin Default: http://localhost:3000 Resources: AuthorizerFunction: Properties: Environment: # 👈 Variables: SECRET_MANAGER_ID: !Ref SecretMangerId ORIGIN: !Ref Origin
sam deploy
の具体的なコマンドはこちら
SECRET_MANAGER_ID="staging/my-secret" ORIGIN="http://localhost:3000" sam deploy --force-upload --parameter-overrides SecretMangerId=$SECRET_MANAGER_ID Origin=$ORIGIN
Lambda内での取得。dotenvなどはinstallする必要はなかった。
const origin = process.env.ORIGIN; const secretManagerId = process.env.SECRET_MANAGER_ID; if (!origin || typeof origin !== 'string') { throw new Error('ORIGIN is not defined'); } if (!secretManagerId || typeof secretManagerId !== 'string') { throw new Error('SECRET_MANAGER_ID is not defined'); }
aws-sdkでsecretManagerIdを引数に渡すときになぜかエラーが発生したので、toString()
を使用した。
import * as AWS from 'aws-sdk'; export const secretsManagerClient = async (secretManagerId: string) => { try { const secretsManager = new AWS.SecretsManager({ region: 'us-east-1', }); const response = await secretsManager .getSecretValue({ // NOTE: toString()をつけないと { message: 'Invalid name. Must be a valid name containing alphanumeric characters, or any of the following: -/_+=.@!' } が返ってくる SecretId: secretManagerId.toString(), // 👈 ここ }) .promise(); if ('SecretString' in response) { return response.SecretString; } else { throw new Error('Decrypting secrets failed'); } } catch (err) { return JSON.stringify({ err }, null, 2); } };
# Cache Time to live in seconds # https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-property-api-lambdatokenauthorizationidentity.html Identity: ReauthorizeEvery: 0
CorsConfiguration - AWS Serverless Application Model