spring-attic / spring-security-oauth

Support for adding OAuth1(a) and OAuth2 features (consumer and provider) for Spring web applications.
http://github.com/spring-projects/spring-security-oauth
Apache License 2.0
4.69k stars 4.05k forks source link

Parsing issue when redirect_uri is "urn:ietf:wg:oauth:2.0:oob" #1822

Open alejo-17 opened 4 years ago

alejo-17 commented 4 years ago

Summary

I'm migrating spring oauth2 version from 2.0.9.RELEASE to the latest one 2.4.0.RELEASE and it looks like the new implementation of this method https://github.com/spring-projects/spring-security-oauth/blob/2.4.0.RELEASE/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/DefaultRedirectResolver.java#L210 does not support a redirect_uri parameter "urn:ietf:wg:oauth:2.0:oob".

Actual Behavior

This exception when returning the redirect_uri: java.net.URISyntaxException: Expected scheme-specific part at index 4: urn: at java.net.URI$Parser.fail(URI.java:2848) at java.net.URI$Parser.failExpecting(URI.java:2854) at java.net.URI$Parser.parse(URI.java:3057) at java.net.URI.(URI.java:820) at org.springframework.web.util.OpaqueUriComponents.toUri(OpaqueUriComponents.java:131) at org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.append(AuthorizationEndpoint.java:494) at org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.appendAccessToken(AuthorizationEndpoint.java:408) at org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.getImplicitGrantResponse(AuthorizationEndpoint.java:343) at org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.authorize(AuthorizationEndpoint.java:174) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:854) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:765) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) at javax.servlet.http.HttpServlet.service(HttpServlet.java:734)

After debugging, I saw an strange behavior on this line: redirectUriBuilder.replaceQuery(requestedRedirectUri.getQuery());

The object "requestedRedirectUri" seems to be null, so all the content of the redirect_uri is replaced with "urn:".

Expected Behavior

Authorize endpoint should return a redirect_uri = "urn:ietf:wg:oauth:2.0:oob".

Configuration

Version

2.4.0.RELEASE It's not happening in 2.0.9.RELEASE.

Sample

public static void main(String args[]) { UriComponentsBuilder redirectUriBuilder = UriComponentsBuilder.fromUriString("urn:ietf:wg:oauth:2.0:oob"); redirectUriBuilder.replaceQuery(null); //Simulating a null behavior redirectUriBuilder.fragment(null); System.out.println(redirectUriBuilder.build().toUriString()); }

alejo-17 commented 4 years ago

I tested a null validation change and it looks like it resolves the issue.

`private String obtainMatchingRedirect(Set redirectUris, String requestedRedirect) { Assert.notEmpty(redirectUris, "Redirect URIs cannot be empty");

    if (redirectUris.size() == 1 && requestedRedirect == null) {
        return redirectUris.iterator().next();
    }

    for (String redirectUri : redirectUris) {
        if (requestedRedirect != null && redirectMatches(requestedRedirect, redirectUri)) {
            // Initialize with the registered redirect-uri
            UriComponentsBuilder redirectUriBuilder = UriComponentsBuilder.fromUriString(redirectUri);

            UriComponents requestedRedirectUri = UriComponentsBuilder.fromUriString(requestedRedirect).build();

            if (this.matchSubdomains) {
                redirectUriBuilder.host(requestedRedirectUri.getHost());
            }
            if (!this.matchPorts) {
                redirectUriBuilder.port(requestedRedirectUri.getPort());
            }

            String redirectUriQuery = requestedRedirectUri.getQuery();

            if (redirectUriQuery != null) {
                redirectUriBuilder.replaceQuery(redirectUriQuery);      // retain additional params (if any)
            }
            redirectUriBuilder.fragment(null);
            return redirectUriBuilder.build().toUriString();
        }
    }`