Edited at

アプリ(Node.js)の認証(OIDC)をALBにやってもらうやつ

More than 1 year has passed since last update.

今さら感ありますがApplication Load Balancerでのユーザー認証機能を試してみました。

JWTの検証で意外にハマってしまったのでメモしておきます。


IdPの設定

今回はOneLoginを利用しました。

用意されている「OpenId Connect app」をCompany Appとして追加すれば終わりと高を括っていたところ、ALBがHTTP 561を返し続け大分ハマりました。

トークンエンドポイントへのリクエストを「Basic」ではなく「POST」に設定しないといけないのでした。1


ALBの設定

AWSのドキュメントとブログを頼りに設定できました。

個人的なポイントとしては以下。


  • ALBはHTTPSでアクセスされるように設定する

  • 認証アクションで入力を求められる各エンドポイントは/.well-known/openid-configurationから確認する2

  • リダイレクトURLにはhttps://<ALBのドメイン名>/oauth2/idpresponseを設定する


アプリケーション実装

上で挙げたブログ曰く、


しかしながら、対象リクエストが改ざんされていないことを担保する JWT ヘッダー上の署名を検証するためのアプリケーションを実装することは依然として重要です。


ということなので、Auth0のnode-jsonwebtokenを利用して検証を行うことにしました。が、ダメ。デコードすらできず。IDトークンがbase64urlエンコードされていないっぽい。

Base64エンコードした後、以下の変換を施したものがbase64urlエンコードらしい3のですがIDトークンに「=」が入ってしまっていました。削除するとデコードできるようになりましたがverifyは失敗します。

対象の文字
変換後

=
削除する

+
-

/
_

そこでjwt.ioにて他のライブラリがないか探しjsrsasignというライブラリを使わせてもらうことにしました。

これで検証とデコードが問題なくできるようになり、あとはAWSのドキュメントに載っているPythonで書かれたサンプルをJavaScriptに移植すれば、出来上がりっ


index.js

const express = require('express');

const fetch = require('node-fetch');
const rs = require('jsrsasign');

const app = express();

app.get('/activate', (req, res) => {
// IDトークンを取得
const token = req.header('x-amzn-oidc-data');

// ヘッダーからkey idを取得
const decodedJwt = rs.KJUR.jws.JWS.parse(token);
const kid = decodedJwt.headerObj.kid;

// 検証用の公開鍵を取得
fetch(`https://public-keys.auth.elb.ap-northeast-1.amazonaws.com/${kid}`).then(response => {
return response.text();
}).then(pem => {
// トークンを検証
const pubKey = rs.KEYUTIL.getKey(pem);
const isValid = rs.KJUR.jws.JWS.verify(token, pubKey, ["ES256"]);
if (isValid) {
res.send('valid');
} else {
res.status(400).send('invalid');
}
});
});

app.get('/', (req, res) => {
res.send('arrived!');
});

app.listen(3000);