#はじめに
SDKをローカルに持ってきてゴニョるサンプルは検索に引っかかるのですが、
クラウド側(Lambda関数内部)で完結するサンプルが見つからない...
よし、ならば投稿してしまえ。
トップ
├ユーザー作成
├ユーザー確認
├ユーザー認証
└__ユーザー認可 ←イマココ__
#注意事項
本稿は、こちらの記事をLambda関数で実現することを目的としています。
兄弟記事とは毛色が違いますので、予めご了承ください。
- Decode and verify Amazon Cognito JWT tokens (Amazon Cognito JWT トークンを復号して検証する)を、JavaScriptで改変したプログラムを使用します。
- npmを使用してライブラリを入手し、Lambdaにアップロードする手順が含まれます。
- Lambda関数でAPI Gateway用のオーソライザーの作成方法を解説する記事ではありません。
- API Gatewayでオーソライザーの設定方法を解説する記事でもありません。→こちらをご覧ください。
#ユーザー認可 (Authorization)
通知されたIDトークンが有効かどうかを判断します。
通常、ユーザー認可はAPI Gatewayのオーソライザーに任せればいいのですが、諸事情によりAPI Gatewayに任せられない場合は、Lambdaに担当させる必要があります。
本稿では、Lambda関数でユーザー認可する方法を解説します。
##ドキュメント
AWSJavaScriptSDKは使用しません。
JSON ウェブトークンの検証
##npm
ユーザー認可に使用する外部ライブラリを入手します。
###外部ライブラリ
npmで入手します。
入手した外部ライブラリはLambdaにアップロードします。
npm i jwk-to-pem --save
npm i jsonwebtoken --save
##Cognito
Cognitoのユーザープールから、ユーザー認可に使用する「公開キー」と「アプリクライアントID」を入手します。
###公開キー
公開キーはこちらにあります。
https://cognito-idp.{リージョン}.amazonaws.com/{プールID}/.well-known/jwks.json
プールIDは、Cognitoのユーザープールの「全般設定」のページから確認できます。
###アプリクライアントID
アプリクライアントIDは、Cognitoのユーザープールの「全般設定>アプリクライアント」のページから確認できます。
##Lambda
関数を一から作成します。
###ソースコード
↓適当な例外をthrowしています。
アプリクライアントIDや発行者(issuer)の文字列は、Lambdaの環境変数に設定すると、プログラムの見通しが良くなると思います。
'use strict';
// ライブラリ
const jwkToPem = require('./node_modules/jwk-to-pem/src/jwk-to-pem.js');
const jsonwebtoken = require('./node_modules/jsonwebtoken');
// 公開キー。 https://cognito-idp.{リージョン}.amazonaws.com/{プールID}/.well-known/jwks.json と同じもの
const jwks = require('./jwks.json');
// JWT形式判定用の正規表現
const jwtRe = /(?<header>.+)\.(?<payload>.+)\.(?<signature>.+)/;
/**
* オーソライザー (トークンを検証する)
* @param {string} token IDトークン
* @returns {Object} ペイロード
*/
module.exports = token => {
// 入力はJWT形式の文字列か?
const match = jwtRe.exec(token);
if (!match) throw 'AuthError';
// ヘッダー情報を取得
const header = JSON.parse(Buffer.from(match.groups.header, 'base64').toString());
// 公開キーから使用するキーを選ぶ
const jwk = jwks.keys.find(k => k.kid == header.kid);
if (!jwk) throw 'AuthError';
// 外部ライブラリを使って署名を確認する
const pem = jwkToPem(jwk);
const claim = jsonwebtoken.verify(token, pem);
// 有効期限の確認
const now = new Date() / 1000;
if (now > claim.exp || now < claim.auth_time) throw 'AuthError';
// 利用者(audience)の確認
if (claim.aud !== '{アプリクライアントID}') throw 'AuthError';
// 発行者(issuer)の確認
if (claim.iss !== 'https://cognito-idp.{リージョン}.amazonaws.com/{プールID}') throw 'AuthError';
// トークン種別の確認
// IDトークンを使う場合の claim.token_use は 'id'
// アクセストークンを使う場合の claim.token_use は 'access'
if (claim.token_use !== 'id') throw 'AuthError';
// ハレて認可された
return claim;
};
↓例外をcatchしていませんが、そのあたりを含めて適当に改変してください。
'use strict';
// オーソライザー
const authorizer = require('./authorizer.js');
/**
* メイン
* @param {Object} event プロキシ統合の情報
* @returns {Promise<HTTPResponse>} HTTPレスポンス
*/
exports.handler = async event => {
// event情報からIDトークンを取得
const token = event.headers['Authorization']; // HTTPヘッダーのAuthorizationにIDトークンがある場合の例
// IDトークンをオーソライザーに投入
const claim = authorizer(token);
// 例外が発生しなければ認可成功
// HTTPレスポンスを返して終了
return {
statusCode: 200,
body: 'Authorization succeeded.',
};
};