且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Spring OAuth 授权服务器需要范围

更新时间:2023-02-16 08:03:11

根据 DefaultOAuth2RequestFactory,如果客户端没有提供作用域,将使用为客户端注册的作用域.

DefaultOAuth2RequestFactory.java

私有集extractScopes(Map requestParameters, String clientId) {设置范围 = OAuth2Utils.parseParameterList(requestParameters.get(OAuth2Utils.SCOPE));ClientDetails clientDetails = clientDetailsS​​ervice.loadClientByClientId(clientId);if ((scopes == null || scopes.isEmpty())) {//如果传入的数据中没有指定作用域,则使用客户端注册的默认值//(规范允许我们在此选项和完全拒绝请求之间进行选择,因此我们将采用//最不讨厌的选择作为默认值).范围 = clientDetails.getScope();}如果(检查用户范围){scopes = checkUserScopes(scopes, clientDetails);}返回范围;}

因此您可以使用默认all"范围或类似的范围配置您的客户端,例如

public void configure(ClientDetailsS​​erviceConfigurer clients) 抛出异常 {客户.inMemory().withClient("client").secret("secret").authorizedGrantTypes("authorization_code", "client_credentials").scopes("all");

We're currently using the Spring OAuth Authorization Server but currently do not use the "scope" parameter from the OAuth specification. This has been a bit of a pain point as the Spring OAuth Authorization Server requires that scope be explicitly required when requesting an authorization code.

From DefaultOAuth2RequestValidator:

if (requestScopes.isEmpty()) {
    throw new InvalidScopeException("Empty scope (either the client or the user is not allowed the requested scopes)");
}

This however goes directly against the OAuth 2.0 specification:

 4.1.1.  Authorization Request

    The client constructs the request URI by adding the following   
 parameters to the query component of the authorization endpoint URI   
 using the "application/x-www-form-urlencoded" format, per Appendix B:

    response_type
          REQUIRED.  Value MUST be set to "code".

    client_id
          REQUIRED.  The client identifier as described in Section 2.2.

    redirect_uri
          OPTIONAL.  As described in Section 3.1.2.

    scope
          OPTIONAL.  The scope of the access request as described by
          Section 3.3.

    state
          RECOMMENDED.  An opaque value used by the client to maintain
          state between the request and callback.  The authorization
          server includes this value when redirecting the user-agent back
          to the client.  The parameter SHOULD be used for preventing
          cross-site request forgery as described in Section 10.12.

Is there an explicit reason why the Spring Authorization Server does this? I know that I can replace the validator with my own but I'm curious as to why this is the default in case I'm missing any understanding other than it being this way for legacy reasons.

Thank you.

EDIT

For those looking for an alternative implementation that follows the specification, here is mine. It simply checks that if the client is restricted to certain scopes, only then the requested scope is required and that the requested scope must be in the list of assigned client scopes. If the client has no assigned scopes, this implementation assumes they are allowed use of any scope (same assumption that is made for resources). Not quite sure yet the implications of this or if it's genuinely correct. Please let me know if it is not.

import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.TokenRequest;

public class OAuth2RequestValidator
    implements org.springframework.security.oauth2.provider.OAuth2RequestValidator {

  @Override
  public void validateScope(final AuthorizationRequest authorizationRequest,
      final ClientDetails client)
      throws InvalidScopeException {
    this.validateScope(authorizationRequest.getScope(), client.getScope());
  }

  @Override
  public void validateScope(final TokenRequest tokenRequest, final ClientDetails client)
      throws InvalidScopeException {
    this.validateScope(tokenRequest.getScope(), client.getScope());
  }

  private void validateScope(
      final Set<String> requestScopes, 
      final Set<String> clientScopes) {
    if (!CollectionUtils.isEmpty(clientScopes)) {
      if (CollectionUtils.isEmpty(requestScopes)) {
        throw new InvalidScopeException(
            "Empty scope (either the client or the user is "
              + "not allowed the requested scopes)");
      }

      for (final String scope : requestScopes) {
        if (!clientScopes.contains(scope)) {
          throw new InvalidScopeException("Invalid scope: " + scope, clientScopes);
        }
      }
    }
  }

}

According to the DefaultOAuth2RequestFactory, if no scope is supplied by the client, the scope registered for the client will be used.

DefaultOAuth2RequestFactory.java

private Set<String> extractScopes(Map<String, String> requestParameters, String clientId) {
    Set<String> scopes = OAuth2Utils.parseParameterList(requestParameters.get(OAuth2Utils.SCOPE));
    ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);

    if ((scopes == null || scopes.isEmpty())) {
        // If no scopes are specified in the incoming data, use the default values registered with the client
        // (the spec allows us to choose between this option and rejecting the request completely, so we'll take the
        // least obnoxious choice as a default).
        scopes = clientDetails.getScope();
    }

    if (checkUserScopes) {
        scopes = checkUserScopes(scopes, clientDetails);
    }
    return scopes;
}

So you could configure your client with a default scope of "all" or something similar e.g.

public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory()
            .withClient("client").secret("secret")
            .authorizedGrantTypes("authorization_code", "client_credentials")
            .scopes("all");