こんにちは!
ServerlessFramework での AWS インフラ構築は、私たちにとってデファクトスタンダードです。
Serverless Framework 関連のプラグインを活用すれば AppSync 構築をはじめとした大部分の構築を簡略化できますし、Cloud Formation を自由に書けるので拡張性も十分です。
本記事では、Serverless Framwork で実装する API Gateway の Authorizer のについてソースコード交え解説します。
functions の event プロパティに http イベントを設定することで、API Gateway が自動的に構築されます。
Cloud Formationの記述などは基本的に必要ありません。
# serverless.yml
functions:
index:
handler: handler.hello
events:
- http: GET hello
Cognito User Pool を認可方式に指定しますので、Coognito も紹介します。尚、Lambda のソースコードにはお好きな処理を記述してください。
service: ApiGatewayWithAuthorization
provider:
name: aws
runtime: nodejs12.x
stage: ${opt:stage,"dev"}
region: ap-northeast-1
profile: ragate
iamRoleStatements:
- Effect: 'Allow'
Action:
- 'lambda:*'
- 'cognito:*'
- 'apigateway:*'
Resource:
- '*'
plugins:
- serverless-webpack
custom:
webpack:
includeModules: true
packager: 'npm'
functions: ${file(./resources/functions.yml)}
resources:
- ${file(./resources/cognito.yml)}
- ${file(./resources/apigateway-authorizer.yml)}
package:
individually: true
exclude:
- node_modules/**
- resources/**
- package-lock.json
- package.json
- webpack.config.js
- yarn.lock
- README.md
- .git/**
- tmp/**
# include:
Resources:
ApiGatewayWithAuthorizationUserPool:
Type: 'AWS::Cognito::UserPool'
Properties:
AccountRecoverySetting: # パスワード忘れの際の復帰方法
RecoveryMechanisms:
- Name: 'verified_email'
Priority: 1
AdminCreateUserConfig:
AllowAdminCreateUserOnly: false # 管理者権限を持つLambdaでのみユーザー作成を実行するので有効にする(ユーザー自身がSDKでサインアップする際はfalseにすること)
InviteMessageTemplate:
EmailMessage: 'Your username is {username} and temporary password is {####}.'
EmailSubject: 'Your temporary password'
SMSMessage: 'Your username is {username} and temporary password is {####}.'
AliasAttributes:
- email
- preferred_username # 独自のユーザー名称でログインできる仕様なので入れておく
AutoVerifiedAttributes:
- email # emailは自動的に検証し、保有を検証するうようにしておく
DeviceConfiguration: # デバイスはいったん記憶させない、今後MFA認証を実装する場合に有効にするのを検討
ChallengeRequiredOnNewDevice: false
DeviceOnlyRememberedOnUserPrompt: false
EmailConfiguration:
EmailSendingAccount: COGNITO_DEFAULT
EmailVerificationMessage: 'Your verification code is {####}.'
EmailVerificationSubject: 'Your verification code'
MfaConfiguration: OFF
Policies:
PasswordPolicy:
MinimumLength: 8
RequireLowercase: false
RequireNumbers: false
RequireSymbols: false
RequireUppercase: false
TemporaryPasswordValidityDays: 365 # 管理者によるパスワードリセットは実装しないので、最長にしておく(セキュリティホールになる様子であれば今後対応を検討)
Schema:
- AttributeDataType: String
DeveloperOnlyAttribute: false
Mutable: true # OIDC, SAML2などのIDプロバイダーを追加し属性マッピングする可能性があるので変更可能にしておく
Name: email
Required: true
SmsAuthenticationMessage: 'Your verification code is {####}.'
SmsVerificationMessage: 'Your verification code is {####}.'
UsernameConfiguration:
CaseSensitive: true # 大文字と小文字を区別する
UserPoolAddOns:
AdvancedSecurityMode: AUDIT # いったん監視のみし、CloudWatchにてログ確認のみにする、今後セキュリティ強化のニーズが高まったらENFORCEDへ設定したい
UserPoolName: ${self:service}-${self:provider.stage}-user-pool
UserPoolTags:
Service: ${self:service}-${self:provider.stage}
VerificationMessageTemplate:
DefaultEmailOption: CONFIRM_WITH_CODE # 認証リンクの送信は行わない、コード送信のみ送信
EmailMessage: 'Your verification code is {####}.'
EmailSubject: 'Your verification code'
SmsMessage: 'Your verification code is {####}.'
ApiGatewayWithAuthorizationUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
CallbackURLs:
- 'http://localhost:3000'
ClientName: ${self:service}-${self:provider.stage}-user-pool-client
DefaultRedirectURI: 'http://localhost:3000'
ExplicitAuthFlows:
- ALLOW_USER_PASSWORD_AUTH
- ALLOW_ADMIN_USER_PASSWORD_AUTH
- ALLOW_USER_SRP_AUTH
- ALLOW_REFRESH_TOKEN_AUTH
# GenerateSecret: Boolean
LogoutURLs:
- 'http://localhost:3000'
PreventUserExistenceErrors: ENABLED # Cognitoから返却するエラーは具体的にしておく(エラーによってUIを動的にしたい可能性があるので...)
ReadAttributes:
- email
- preferred_username
RefreshTokenValidity: 10 # リフレッシュトークンの生存日数
SupportedIdentityProviders:
- COGNITO # 今後拡張していくが、今はCognitoのみでOK
UserPoolId:
Ref: ApiGatewayWithAuthorizationUserPool
WriteAttributes: # 外部のIDプロバイダー利用時に属性を書き込む可能性があるので使用している属性情報の書き込みが可能にしておく
- email
- preferred_username
Resources:
ApiGatewayWithAuthorizationAuthorizer:
Type: AWS::ApiGateway::Authorizer
DependsOn:
- ApiGatewayRestApi # 暗黙的に適用されるが一応入れておく
Properties:
Name: ApiGatewayWithAuthorizationAuthorizer
RestApiId:
Ref: ApiGatewayRestApi
IdentitySource: method.request.header.Authorization
Type: COGNITO_USER_POOLS
ProviderARNs:
- { Fn::GetAtt: [ApiGatewayWithAuthorizationUserPool, Arn] }
同じスタックで構築するAPI Gatewayの Authorizer のID を指定し認証に指定します。逆に指定しない API は認証不要となります。
getWithAuth:
handler: src/functions/example/get.handler
events:
- http:
path: /example_with_auth
method: get
integration: lambda
authorizer:
type: COGNITO_USER_POOLS
authorizerId: !Ref ApiGatewayWithAuthorizationAuthorizer
getWithoutAuth:
handler: src/functions/example/get.handler
events:
- http:
path: /example_without_auth/
method: get
get.js
// src/functions/example/get.handler
export async function handler(event, context, callback) {
callback(null, {
statusCode: 200,
body: JSON.stringify({
result: 'success'
}),
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': 'true'
},
})
}
上記のコードをデプロイしたら、AWS の管理コンソールへアクセスし、API Gateway のエンドポイントへリクエストしてみてください。
getWithAuth リソースは、Authorization ヘッダーに ユーザープールトークンを設定しないと 401 がレスポンスされるはずです。
実運用の際にはユーザーグループを活用して、グループ単位で API アクセスに認可を設定するケースが多数です。今後ソースコードを交え紹介していきます。
API Gateway、AppSync に関する開発はお気軽に相談ください。
スモールスタート開発支援、サーバーレス・NoSQLのことなら
ラーゲイトまでご相談ください
低コスト、サーバーレスの
モダナイズ開発をご検討なら
下請け対応可能
Sler企業様からの依頼も歓迎