2024年1月24日水曜日

OpenID Providerを作る)認可エンドポイントでクライアントの登録状態を検証する

こんにちは、富士榮です。

前回はトークンエンドポイントでクライアント認証を実装したので、今回は認可エンドポイントに戻り、クライアントの登録状態および指定されたredirect_uriが正しいかどうかの検証処理を入れてみます。


その前にこれまでのおさらいです。


さて始めていきましょう。
今回は検証ということでエラー処理に関する仕様を中心に確認していく必要があります。
ポイントになるのは、この辺りです。

これらの章から分かることは、以下の2点です。
  • redirect_uriが不正ならOpenID Providerが直接エラーを返し、それ以外の場合はredirect_uriへコールバックすること(関連する記載:Redirection URI が無効でない限り, Authorization Server は適切なエラーと state パラメータで Authorization Request にて指定された Redirection URI に Client を返す.)
  • エラーレスポンスは各フローのredirect_uriへのコールバック方法に従って返却すること(要はコードフローなら&、インプリシット・ハイブリッドなら#で返せ、ということです)

また、redirect_uriのマッチングルールは認証リクエストに関する仕様に以下のように定められていますので、この辺りも確認ポイントです。
この URI は, Client が OpenID Provider に対して事前に登録済みの Redirection URI のいずれかと完全一致しなければならない (MUST). マッチングルールは [RFC3986] (Simple String Comparison) の Section 6.2.1 に従うこと.

要するに完全一致が必要ってことですね。ワイルドカードが使いたい、という要望はよく聞かれますがいわゆるオープンリダイレクトになってしまう可能性があることから仕様上は完全一致が要求されます。 


それらを踏まえ実装してみます。

まずはエラーの返却方法がresponse_typeによって異なることからクライアント検証を行う前にresponse_typeを判別しておく必要があります。これは前回のスコープのところで実装したロジックをそのまま使います。

// エラーの返却方法にも関連するためresponse_typeの判別は最初にやっておく
// response_typeの取得
const response_types = req.query.response_type.split(" ");

次にこれもトークンエンドポイントのところで実装したクライアント情報の取得ロジックを流用して登録状態を判別します。

// - client_idの登録状態の確認
// クライアント情報の読み取り
const clients = JSON.parse(fs.readFileSync(path.resolve(__dirname, "../../database/clients.json")));
// クライアント登録状態の確認
const client = clients.find(i => i.client_id === req.query.client_id);
if(typeof client === "undefined"){
// クライアントが未登録
res.redirect(req.query.redirect_uri + errors.errorOnAuthZ(response_types, "invalid_request", "unknown_client_id", req.query.state));
}else{

エラーハンドリングは共通処理となるのでこんな感じで外部化しておきます。

exports.errorOnAuthZ = function(response_types, error_code, error_description, state) {
// implicitもしくはHybridを判定するフラグ(フラグメントでレスポンスを返すかどうかの判定)
let mode;
if(response_types.includes("token") || response_types.includes("id_token")){
// ImplicitもしくはHybridフロー
mode = "#";
}else{
// codeフロー
mode = "&";
}
return(mode + "error=" + error_code + "&error_description=" + error_description + "&state=" + state);
}

次はredirect_uriのマッチングです。ここも登録状態の確認とほぼほぼ類似のロジックですが、未登録のredirect_uriの場合はOpenID Providerから直接エラーを返却するようにしています。

// redirect_uriがclient設定に合致していることの確認
if(!client.redirect_uris.includes(req.query.redirect_uri)){
// redirect_uri未登録
// redirect_uriが不正なのでOPから直接エラーを返却する
res.status = 400;
res.json({
error: "invalid_request",
error_description: "unknown_redirect_uri",
state: req.query.state
})
}else{


と、こんなところです。

ここまでの実装もこちらにプッシュしてありますので参考にしてください。


0 件のコメント: