LoginSignup
26
16

More than 3 years have passed since last update.

新しい Spring Authorization Server について

Last updated at Posted at 2020-12-03

はじめに

Spring Security プロジェクトは、認可サーバーの実装を今後サポートしない旨、2019 年 11 月 14 日付の『Spring Security OAuth 2.0 Roadmap Update』でアナウンスしました。しかしその後、一部の有志が Spring の認可サーバー実装プロジェクト『Spring Authorization Server』を開始しました。この記事は、そのプロジェクトに対する警鐘となります。

OAuth 2.0 Multiple Response Type Encoding Practices

2012 年 10 月の RFC 6749(The OAuth 2.0 Authorization Framework)のリリースから 1 年 4 ヶ月後の 2014 年 2 月、OAuth 2.0 Multiple Response Type Encoding Practices(以降 OAuth.Responses)という仕様が 2014 年 2 月に公開されました。その 9 ヶ月後の 2014 年 11 月に公開された OpenID Connect Core 1.0 は、当仕様を大前提としています。

公開日 仕様
2012 年 10 月 RFC 6749(The OAuth 2.0 Authorization Framework)
2014 年 02 月 OAuth 2.0 Multiple Response Type Encoding Practices
2014 年 11 月 OpenID Connect Core 1.0

OAuth.Responses が認可サーバーの実装に与える影響は甚大で、Spring Security OAuth が行き詰まった理由の一つだと私は考えています(参考:Issue #619 Handling additional response_types)。

OAuth.Responses により、認可リクエストの response_type パラメーターの処理方法が大きく変わりました。それまでは、つまりは RFC 6749 の時点では、「response_type の値は code または token のどちらかになる」という前提で認可サーバーの実装を書いていればよかったのですが、OAuth.Responses 以降は、「response_type は、code/id_token/token の任意の組合せ、もしくは単独の none」という複雑な状況に対応しなければならなくなりました。

response_type
RFC 6749 時点 code もしくは token
OAuth.Responses 以降 code/id_token/token の任意の組合せ、もしくは単独の none

仕様上、RFC 6749 は response_type に複数の値を入れることを許容しています。しかし、認可レスポンスパラメーターを置く場所に関して、codetoken で仕様が衝突するため、衝突の解決方法が提示されていない段階では(OAuth.Responses が規定されるまでは)、codetoken が同時に response_type に含まれることを想定することは困難でした。

RFC 6749 の時点では、response_type の処理の実装は、下記の仮想コードのように、response_type の値が特定の値と「一致するかどうか」を調べていれば済みました。

if (response_type == 'code') {
    // 認可コードフロー (RFC 6749 Section 4.1)
    // 認可コードを発行する
}
else if (response_type == 'token') {
    // インプリシットフロー (RFC 6749 Section 4.2)
    // アクセストークンを発行する
}
else {
    // response_type の値が不正
}

一方、OAuth.Responses 以降は、response_type に特定の値が「含まれているか」を調べる必要がでてきます。

if ('code' in response_type) {
    // 認可コードを発行する
}

if ('token' in response_type) {
    // アクセストークンを発行する
}

if ('id_token' in response_type) {
    // ID トークンを発行する
}

if ('none' in response_type) {
    // 何も発行しない. response_type  none 以外が
    // 含まれていればエラー.
}

response_type に複数の値が含まれるということは、認可エンドポイントから複数のトークンが発行されうるということを意味します。例えば response_typecode id_token token の場合、認可エンドポイントからは認可コード、ID トークン、アクセストークン、の三つが発行されます。詳細は『OpenID Connect 全フロー解説』を参照してください。

response_type 認可コード ID トークン アクセストークン
code :heavy_check_mark:
token :heavy_check_mark:
id_token :heavy_check_mark:
id_token token :heavy_check_mark: :heavy_check_mark:
code id_token :heavy_check_mark: :heavy_check_mark:
code token :heavy_check_mark: :heavy_check_mark:
code id_token token :heavy_check_mark: :heavy_check_mark: :heavy_check_mark:
none

ID トークンと同時に認可コードやアクセストークンが発行される場合、それぞれのハッシュ値を c_hash クレームや at_hash クレームの値として ID トークンに含めなければならないというルールなどもあり(OIDC Core 1.0 Section 3.3.2.11)、OAuth.Responses により認可エンドポイントの実装はかなり複雑になります。

Spring Authorization Server の実装

Spring Security が認可サーバーの実装を今後サポートしないことを発表してから約 9 ヶ月後の 2020 年 8 月 21 日、Spring.IO のブログ『Get the very first bits of Spring Authorization Server 0.0.1 !』により、Spring 用の認可サーバー実装のバージョン 0.0.1 がアナウンスされました。この実装に大きな期待を寄せている方々もいらっしゃるでしょう。

この実装の response_type の処理は OAuth2AuthorizationEndpointFilter.java にあり、現時点では次のようになっています。

// response_type (REQUIRED)
if (!StringUtils.hasText(authorizationRequestContext.getResponseType()) ||
        authorizationRequestContext.getParameters().get(OAuth2ParameterNames.RESPONSE_TYPE).size() != 1) {
    authorizationRequestContext.setError(
            createError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.RESPONSE_TYPE));
    return;
} else if (!authorizationRequestContext.getResponseType().equals(OAuth2AuthorizationResponseType.CODE.getValue())) {
    authorizationRequestContext.setError(
            createError(OAuth2ErrorCodes.UNSUPPORTED_RESPONSE_TYPE, OAuth2ParameterNames.RESPONSE_TYPE));
    return;
}

ご覧の通り、OAuth.Responses 仕様を念頭に置いていないことは明らかで、Spring Security OAuth プロジェクトの OpenID Connect サポートが行き詰まったのと同じ過ちを繰り返しています

このコードが参照している OAuth2AuthorizationResponseType.CODE ですが、これは Spring Security 本体に含まれています。OAuth2AuthorizationResponseType クラスには現在、CODETOKEN の 2 つのクラス変数が定義されています。少なくともここに ID_TOKEN が入ってこないと、新 Spring Authorization Server プロジェクトで OpenID Connect をサポートすることはできないでしょう。

OAuth2AuthorizationEndpointFilter.java の認可リクエストパラメーターの処理順序を見ると、scope よりも前に redirect_uri がチェックされています。この処理順序ですと、「scopeopenid が含まれる場合は redirect_uri が必須」という OpenID Connect Core 1.0 の要求事項のチェックができません。新 Spring Authorization Server プロジェクトが OpenID Connect を考慮していないことが分かります。

当然、OpenID Connect Core 1.0 で規定されているリクエストオブジェクトの実装は含まれていません。しかし、リクエストオブジェクトはリクエストパラメーターの処理に大きな影響を与えるので、設計段階から考慮に入れておく必要があります。なお、Financial-grade APIFAPI)の Part 2 でリクエストオブジェクトが必須になっていることからも分かるとおり、高いセキュリティが要求される環境では、リクエストオブジェクトの実装が必要です。リクエストオブジェクトの詳細は『JAR (JWT Secured Authorization Request) に関する実装者の覚書』を参照してください。

何年も前に明らかになっている問題点を繰り返していること、ソースコードの読みにくさ、認可画面の HTML を OAuth2AuthorizationEndpointFilter.java にハードコーディングするセンス、そもそも認可エンドポイントでの処理を『フィルター』として実装しようとするアプローチ、を鑑みれば、新 Spring Authorization Server の実装品質が良くないことは明らかです。

新 Spring Authorization Server プロジェクトに期待をしている方々には、Spring の名を過信せず、ソースコードの品質を確認した上で利用の可否を判断するよう、助言させていただきたく思います。

代替プロジェクト

オープンソースに限っても他に選択肢はいろいろあります。中には、正式に OpenID 認証を取得し、商用レベルの品質のものもあります。OpenID Foundation のウェブサイトにライブラリ・製品・ツールのリストがあるので、そちらで要件に合うものを探されるのが良いと思います。

26
16
5

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
26
16