はじめに
AWSでAPIを構築する際、必ずといっていいほど登場するのがAmazonAPIGatewayですよね。
APIGatewayの認証ってどうされてますか?
- 別に必要ないからしてない
- APIKeyを使っている
- CognitoをAuthorizerにしてトークンの検証をする
- LambdaAuthorizerでトークンの検証をする
など色々なパターンがあると思います。今回は最後に書いた「LambdaAuthorizerでトークンの検証をする」について説明して、その構成をServerlessFrameworkでデプロイするところまでを書いていきます。
登場人物
JSON Web Token (JWT)
JSONをベースとしたトークン。
「ヘッダー / ペイロード / 署名」といった3つのセクションから構成されていて、署名はヘッダーとペイロードのハッシュされた組み合わせです。
hogehoge.foofoofoo.barbarbarbar
のような文字列です。
ServerlessFramework
Serverlessなアーキテクチャを構築するためのフレームワーク。AWSだけじゃなくAzureやGCPなど他のクラウドサービスでも利用することができる。
詳しくは 公式ページ で確認してみてください。
構成
Lambda Authorizer
JWTの検証をLambda内で行っていきます。
今回はNode.js(TypeScript)での例を書いています。
import { Handler, Context, Callback, CustomAuthorizerEvent } from "aws-lambda"
import * as jsonwebtoken from "jsonwebtoken"
import jwkToPem from "jwk-to-pem"
import jwk from "./jwk"
const pem = jwkToPem(jwk as any)
export const handler: Handler = async (
event: CustomAuthorizerEvent,
_context: Context,
callback: Callback
): Promise<any> => {
console.log(JSON.stringify(event))
jsonwebtoken.verify(
event.authorizationToken,
pem,
{
algorithms: ["RS256"]
},
(err: jsonwebtoken.VerifyErrors, decodedToken: object | string) => {
if (err) {
callback(null, {
principalId: 1,
policyDocument: {
Version: "2012-10-17",
Statement: [
{
Action: "execute-api:Invoke",
Effect: "Deny",
Resource: event.methodArn
}
]
},
context: {
messagge: "Custom Error Message"
}
})
} else {
console.log(decodedToken)
callback(null, {
principalId: 1,
policyDocument: {
Version: "2012-10-17",
Statement: [
{
Action: "execute-api:Invoke",
Effect: "Allow",
Resource: event.methodArn
}
]
},
context: {
messagge: "Custom Allow Message"
}
})
}
}
)
}
解説
JWT検証で使うモジュールをインストール
$ yarn add jsonwebtoken jwk-to-pem
$ yarn add -D @types/jsonwebtoken @types/jwk-to-pem
jsonwebtoken
がJWTトークンの検証をするライブラリです。
jwt-to-pem
はJWTトークンを検証するための JSON Web Key (JWK) をpem形式に変換するライブラリです。
JWT検証
jsonwebtoken
の verify
というメソッドを使って検証をします。
jsonwebtoken.verify(
event.authorizationToken, // requestHeader内のToken
pem, // JWKから生成したpem
{ algorithms: ["RS256"] },
(err: jsonwebtoken.VerifyErrors, decodedToken: object | string) => {}
)
( Cognitoを使っている場合のJWK取得 )
Cognitoが発行したJWTの検証に使うJWKは https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json
から取得する事ができます。
そのJSONを何かしらの方法でimportしてください。今回の例はTSファイルにしてimportしています。
また上記のURLからJWKを取得すると2つKeyが入っていますが、最初の一つを利用すればいいです。
export default {
alg: "RS256",
e: "XXXX",
kid: "xxxxxxxxxxxxxx",
kty: "RSA",
n: "xxxxxxxxxxxxxx",
use: "sig"
}
検証後の処理
CallbackでAPIGatewayにレスポンスを返して行きます。
Statement内のEffectが Allow
か Deny
かでその後の処理を判断させます。
// 成功時
callback(null, {
principalId: 1,
policyDocument: {
Version: "2012-10-17",
Statement: [
{
Action: "execute-api:Invoke",
Effect: "Allow",
Resource: event.methodArn
}
]
},
context: {
messagge: "Custom Allow Message"
}
})
// 失敗時
callback(null, {
principalId: 1,
policyDocument: {
Version: "2012-10-17",
Statement: [
{
Action: "execute-api:Invoke",
Effect: "Deny",
Resource: event.methodArn
}
]
},
context: {
messagge: "Custom Error Message"
}
})
ServerlessFrameworkでデプロイする
プロジェクト作成
LambdaのソースコードをTypeScriptで書いているので、TypeScript用のテンプレートを使ってプロジェクトを作成します。
$ npx -p serverless sls create -t aws-nodejs-typescript -p example-serverless-ts
serverless.yml
AuthorizerをつけたいAPIGatewayをイベントとするfunctionの中に authorizer
を追加していきます。
-
name
はAuthorizerとなるLambdaです。同じyml内で定義している場合はこのname
で参照することができます。外部でデプロイされているLambdaをAuthorizerにしたい場合は、arn
に対してそのLambdaのARNを指定します。 -
identitySource
はTokenをどこを参照すれば習得できるのかという設定です。この場合はリクエストヘッダー内のAuthorization
を参照するようになっています。 -
type
はAuthorizerとなるLambdaのeventに渡される値の設定です。単純にTokenを取得したいだけならtoken
がおすすめです。リクエストの内容をすべて取得したい場合はrequest
を指定します。
authorizer:
name: authorizer
identitySource: method.request.header.Authorization
type: token
全体としてはこんな感じになります。
service:
name: lambdda-node-ts
plugins:
- serverless-webpack
provider:
name: aws
runtime: nodejs10.x
region: ap-northeast-1
functions:
authorizer:
handler: authorizer.handler
index:
handler: index.handler
events:
- http:
method: get
path: hello
cors: true
authorizer:
name: authorizer
identitySource: method.request.header.Authorization
type: token
あとは sls deploy
でデプロイすれば作業は完了です。
さいごに
今回はLambdaAuthorizerを紹介しました。
トークンの検証にLamdbaを使うことで、トークンの検証以外にもAPIに対するリクエストのログを取ることができたり、怪しいリクエストが来た場合にメール送信などカスタマイズすることができます。
Lambdaを挟むことでかなりできることの幅が広がります!
ではまた!!