8
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

LambdaでCognito認証(ユーザー認可)

Last updated at Posted at 2020-02-28

#はじめに

SDKをローカルに持ってきてゴニョるサンプルは検索に引っかかるのですが、
クラウド側(Lambda関数内部)で完結するサンプルが見つからない...
よし、ならば投稿してしまえ。

トップ
ユーザー作成
ユーザー確認
ユーザー認証
└__ユーザー認可 ←イマココ__

#注意事項
本稿は、こちらの記事をLambda関数で実現することを目的としています。
兄弟記事とは毛色が違いますので、予めご了承ください。

#ユーザー認可 (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
JWT
プールIDは、Cognitoのユーザープールの「全般設定」のページから確認できます。
プールID.png

###アプリクライアントID
アプリクライアントIDは、Cognitoのユーザープールの「全般設定>アプリクライアント」のページから確認できます。
アプリクライアントID

##Lambda
関数を一から作成します。

###環境設定
最終的には次のような環境にします。
lambda環境設定_.png

###ソースコード
↓適当な例外をthrowしています。
アプリクライアントIDや発行者(issuer)の文字列は、Lambdaの環境変数に設定すると、プログラムの見通しが良くなると思います。

authorizer.js
'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していませんが、そのあたりを含めて適当に改変してください。

index.js
'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.',
  };
};
8
12
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?