web-dev-qa-db-ja.com

Spring Security OAuth 2.0-認証コードの付与には常にクライアントシークレットが必要

仕様によると、認証コードの付与を使用したトークンのリクエストは、_client_id_がリクエストに含まれている限り、認証する必要はありませんand _client_id_はコードの生成に使用されたものと同じもの。ただし、Spring Security OAuth 2.0の実装では、クライアントにシークレットが割り当てられていない場合でも、_/oauth/token_エンドポイントで基本認証が常に必要であるように見えます。

ClientDetailsインターフェースのisSecretRequired()メソッドにより、シークレットなしでクライアントを許可するためのサポートがあるようです。シークレットのないクライアントを_/oauth/token_ URLで認証できるようにするには、何をする必要がありますか?

4.1.3。アクセストークンリクエスト

クライアントは、を送信してトークンエンドポイントにリクエストを送信します
「application/x-www-form-urlencoded」を使用した次のパラメーター
HTTPでUTF-8の文字エンコードを使用した付録Bの形式
リクエストエンティティ-本体:

grant_typeが必要です。値は「authorization_code」に設定する必要があります。

コードが必要です。認証サーバーから受信した認証コード。

セクション4.1.1で説明されているように、「redirect_uri」パラメータが承認リクエストに含まれていて、それらの値が同一である必要がある場合は、redirect_uriが必要です。

セクション3.2.1で説明されているように、クライアントが認証サーバーで認証されていない場合は、client_idが必要です。

クライアントタイプが機密であるか、クライアントにクライアント資格情報が発行された(または他の認証要件が割り当てられた)場合、
クライアントは、説明されているように承認サーバーで認証する必要があります
セクション3.2.1。

10
quintonm

以下のコードサンプルに示すように、基本認証の代わりにフォームパラメータを使用してクライアントを認証するには、allowFormAuthenticationForClients()メソッドを使用します。

_class AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {

    @Override
    void configure(AuthorizationServerSecurityConfigurer security) {
        security
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients()
    }
}
_

allowFormAuthenticationForClients()メソッドはClientCredentialsTokenEndpointFilterの追加をトリガーし、フォームパラメーターによる認証を可能にします。

10
quintonm

Springでは、空のシークレットを使用してOAuth2クライアントを定義できます。これらは、「パブリック」クライアント、または秘密を保持できないクライアントと見なすことができます。 Javascriptアプリ、モバイルアプリなどを考えてみてください。通常、クライアントシークレットをそこに保存する必要はありません。

ご指摘のとおり、OAuth2仕様によれば、トークンエンドポイントはこれらのパブリッククライアントにシークレットを要求しないことを選択できます。

したがって、Springでは、空のシークレットを使用してOAuth2クライアントを定義し、限定された一連の付与タイプ(authorization_codeおよびrefresh_token)用に構成するだけです。

Spring Security実装トークンURLは、その特定のOAuth2クライアントのクライアントシークレットなしでトークン交換を受け入れます。

5
ddewaele

この問題を解決するには、クラスのメソッドloadUserByUsernameを参照してください:org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService

if (clientSecret == null || clientSecret.trim().length() == 0) {
   clientSecret = this.emptyPassword;
}

おそらくあなたの場合、emptyPasswordはパスワードエンコーダーによって空のエンコードされたパスワードで初期化されていません。不足しているパスワードエンコーダーをAuthorizationServerConfigurerAdapterに設定します。

@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
    oauthServer.passwordEncoder(passwordEncoder);
}
1
apo

当初、私は受け入れられた答えと同様の設定をしていました。これは間違いなくこれを機能させるための前提条件です。しかし、欠けているのは、パスワードを単にnullに設定することはできないということです。たとえば、次のように、空のパスワードに設定する必要があります。

String secret = PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("");
clientDetails.setClientSecret(secret);

これを行わない場合でも、401を取得できます。