VYPR
High severityNVD Advisory· Published Oct 18, 2018· Updated Sep 17, 2024

Privilege Escalation in spring-security-oauth2

CVE-2018-15758

Description

Spring Security OAuth Authorization Server with a custom approval endpoint is vulnerable to privilege escalation via a crafted request that modifies the saved authorization request.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Spring Security OAuth Authorization Server with a custom approval endpoint is vulnerable to privilege escalation via a crafted request that modifies the saved authorization request.

Vulnerability

A privilege escalation vulnerability exists in Spring Security OAuth versions 2.3 prior to 2.3.4, 2.2 prior to 2.2.3, 2.1 prior to 2.1.3, 2.0 prior to 2.0.16, and older unsupported versions. The issue occurs when an application acts as an Authorization Server (e.g., @EnableAuthorizationServer) and uses a custom approval endpoint that declares AuthorizationRequest as a controller method argument. A malicious user or attacker can craft a request to the approval endpoint that can modify the previously saved authorization request, leading to privilege escalation on the subsequent approval. The vulnerability does not affect applications using the default approval endpoint or those acting only as a Resource Server or Client [1][2][3][4].

Exploitation

An attacker needs network access to the Authorization Server's approval endpoint and the ability to craft a malicious HTTP request. The attack involves sending a specially crafted request to the custom approval endpoint that includes parameters that alter the stored AuthorizationRequest object. This can be done without prior authentication if the endpoint is publicly accessible, or after obtaining a valid authorization request in the OAuth flow. The exact sequence: the attacker obtains a valid authorization request (e.g., by initiating an OAuth flow), then sends a modified request to the approval endpoint that changes the scope, client ID, or other attributes of the saved request, thereby escalating their privileges upon approval [1][2][3][4].

Impact

On successful exploitation, an attacker can achieve privilege escalation within the OAuth authorization flow. This could allow the attacker to gain access to resources or scopes that they should not be authorized for, potentially leading to information disclosure, unauthorized actions, or further compromise of the OAuth-protected resources. The impact is limited to applications that meet the specific configuration criteria; default configurations are not affected [1][2][3][4].

Mitigation

Users should upgrade to patched versions: 2.3.4, 2.2.3, 2.1.3, or 2.0.16. The fix involves validating the authorization request on approval, as implemented in commits 4082ec7, f92223a, 6237766, and ddd65cd [1][2][3][4]. For users unable to upgrade immediately, a workaround is to avoid using custom approval endpoints that declare AuthorizationRequest as a controller method argument, or to implement additional validation of the authorization request parameters manually. No known exploitation in the wild has been reported at the time of disclosure.

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.springframework.security.oauth:spring-security-oauth2Maven
>= 2.0.0, < 2.0.162.0.16
org.springframework.security.oauth:spring-security-oauth2Maven
>= 2.1.0, < 2.1.32.1.3
org.springframework.security.oauth:spring-security-oauth2Maven
>= 2.2.0, < 2.2.3.RELEASE2.2.3.RELEASE
org.springframework.security.oauth:spring-security-oauth2Maven
>= 2.3.0, < 2.3.4.RELEASE2.3.4.RELEASE

Affected products

2

Patches

8
b31d4d949f4e

Release version 2.0.16.RELEASE

34 files changed · +36 36
  • pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
       <name>OAuth for Spring Security</name>
       <description>Parent Project for OAuth Support for Spring Security</description>
       <packaging>pom</packaging>
    -  <version>2.0.16.BUILD-SNAPSHOT</version>
    +  <version>2.0.16.RELEASE</version>
       <url>http://static.springframework.org/spring-security/oauth</url>
     
       <modules>
    
  • samples/oauth2/sparklr/pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     		<relativePath>../../..</relativePath>
     	</parent>
     
    
  • samples/oauth2/tonr/pom.xml+1 1 modified
    @@ -6,7 +6,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     		<relativePath>../../..</relativePath>
     	</parent>
     
    
  • samples/oauth/sparklr/pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     		<relativePath>../../..</relativePath>
     	</parent>
     
    
  • samples/oauth/tonr/pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     		<relativePath>../../..</relativePath>
     	</parent>
     
    
  • samples/pom.xml+1 1 modified
    @@ -4,7 +4,7 @@
       <parent>
         <groupId>org.springframework.security.oauth</groupId>
         <artifactId>spring-security-oauth-parent</artifactId>
    -    <version>2.0.16.BUILD-SNAPSHOT</version>
    +    <version>2.0.16.RELEASE</version>
       </parent>
     
       <artifactId>spring-security-oauth-samples</artifactId>
    
  • spring-security-oauth2/pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<artifactId>spring-security-oauth2</artifactId>
    
  • spring-security-oauth/pom.xml+1 1 modified
    @@ -4,7 +4,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<artifactId>spring-security-oauth</artifactId>
    
  • tests/annotation/approval/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/client/pom.xml+1 1 modified
    @@ -11,7 +11,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<properties>
    
  • tests/annotation/common/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/custom-authentication/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/custom-grant/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/form/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/jaxb/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/jdbc/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/jpa/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/jwt/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/mappings/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/multi/pom.xml+1 1 modified
    @@ -9,7 +9,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/pom.xml+2 2 modified
    @@ -4,7 +4,7 @@
     
     	<groupId>org.demo</groupId>
     	<artifactId>spring-oauth2-tests-parent</artifactId>
    -	<version>2.0.16.BUILD-SNAPSHOT</version>
    +	<version>2.0.16.RELEASE</version>
         <packaging>pom</packaging>
     
         <modules>
    @@ -39,7 +39,7 @@
     		<dependency>
     			<groupId>org.springframework.security.oauth</groupId>
     			<artifactId>spring-security-oauth2</artifactId>
    -			<version>2.0.16.BUILD-SNAPSHOT</version>
    +			<version>2.0.16.RELEASE</version>
     			<exclusions>
     				<exclusion>
     					<artifactId>jackson-mapper-asl</artifactId>
    
  • tests/annotation/resource/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/ssl/pom.xml+1 1 modified
    @@ -11,7 +11,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/vanilla/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/pom.xml+1 1 modified
    @@ -4,7 +4,7 @@
       <parent>
         <groupId>org.springframework.security.oauth</groupId>
         <artifactId>spring-security-oauth-parent</artifactId>
    -    <version>2.0.16.BUILD-SNAPSHOT</version>
    +    <version>2.0.16.RELEASE</version>
       </parent>
     
       <artifactId>spring-security-oauth-tests</artifactId>
    
  • tests/xml/approval/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/client/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/common/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/form/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/jdbc/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/jwt/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/mappings/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/pom.xml+2 2 modified
    @@ -4,7 +4,7 @@
     
     	<groupId>org.demo</groupId>
     	<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -	<version>2.0.16.BUILD-SNAPSHOT</version>
    +	<version>2.0.16.RELEASE</version>
         <packaging>pom</packaging>
     
         <modules>
    @@ -33,7 +33,7 @@
     		<dependency>
     			<groupId>org.springframework.security.oauth</groupId>
     			<artifactId>spring-security-oauth2</artifactId>
    -			<version>2.0.16.BUILD-SNAPSHOT</version>
    +			<version>2.0.16.RELEASE</version>
     			<exclusions>
     				<exclusion>
     					<artifactId>jackson-mapper-asl</artifactId>
    
  • tests/xml/vanilla/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.0.16.BUILD-SNAPSHOT</version>
    +		<version>2.0.16.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
818957ec6e6f

Release version 2.1.3.RELEASE

34 files changed · +36 36
  • pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
       <name>OAuth for Spring Security</name>
       <description>Parent Project for OAuth Support for Spring Security</description>
       <packaging>pom</packaging>
    -  <version>2.1.3.BUILD-SNAPSHOT</version>
    +  <version>2.1.3.RELEASE</version>
       <url>http://static.springframework.org/spring-security/oauth</url>
     
       <modules>
    
  • samples/oauth2/sparklr/pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     		<relativePath>../../..</relativePath>
     	</parent>
     
    
  • samples/oauth2/tonr/pom.xml+1 1 modified
    @@ -6,7 +6,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     		<relativePath>../../..</relativePath>
     	</parent>
     
    
  • samples/oauth/sparklr/pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     		<relativePath>../../..</relativePath>
     	</parent>
     
    
  • samples/oauth/tonr/pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     		<relativePath>../../..</relativePath>
     	</parent>
     
    
  • samples/pom.xml+1 1 modified
    @@ -4,7 +4,7 @@
       <parent>
         <groupId>org.springframework.security.oauth</groupId>
         <artifactId>spring-security-oauth-parent</artifactId>
    -    <version>2.1.3.BUILD-SNAPSHOT</version>
    +    <version>2.1.3.RELEASE</version>
       </parent>
     
       <artifactId>spring-security-oauth-samples</artifactId>
    
  • spring-security-oauth2/pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<artifactId>spring-security-oauth2</artifactId>
    
  • spring-security-oauth/pom.xml+1 1 modified
    @@ -4,7 +4,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<artifactId>spring-security-oauth</artifactId>
    
  • tests/annotation/approval/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/client/pom.xml+1 1 modified
    @@ -11,7 +11,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<properties>
    
  • tests/annotation/common/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/custom-authentication/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/custom-grant/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/form/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/jaxb/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/jdbc/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/jpa/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/jwt/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/mappings/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/multi/pom.xml+1 1 modified
    @@ -9,7 +9,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/pom.xml+2 2 modified
    @@ -4,7 +4,7 @@
     
     	<groupId>org.demo</groupId>
     	<artifactId>spring-oauth2-tests-parent</artifactId>
    -	<version>2.1.3.BUILD-SNAPSHOT</version>
    +	<version>2.1.3.RELEASE</version>
         <packaging>pom</packaging>
     
         <modules>
    @@ -39,7 +39,7 @@
     		<dependency>
     			<groupId>org.springframework.security.oauth</groupId>
     			<artifactId>spring-security-oauth2</artifactId>
    -			<version>2.1.3.BUILD-SNAPSHOT</version>
    +			<version>2.1.3.RELEASE</version>
     			<exclusions>
     				<exclusion>
     					<artifactId>jackson-mapper-asl</artifactId>
    
  • tests/annotation/resource/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/ssl/pom.xml+1 1 modified
    @@ -11,7 +11,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/vanilla/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/pom.xml+1 1 modified
    @@ -4,7 +4,7 @@
       <parent>
         <groupId>org.springframework.security.oauth</groupId>
         <artifactId>spring-security-oauth-parent</artifactId>
    -    <version>2.1.3.BUILD-SNAPSHOT</version>
    +    <version>2.1.3.RELEASE</version>
       </parent>
     
       <artifactId>spring-security-oauth-tests</artifactId>
    
  • tests/xml/approval/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/client/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/common/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/form/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/jdbc/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/jwt/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/mappings/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/pom.xml+2 2 modified
    @@ -4,7 +4,7 @@
     
     	<groupId>org.demo</groupId>
     	<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -	<version>2.1.3.BUILD-SNAPSHOT</version>
    +	<version>2.1.3.RELEASE</version>
         <packaging>pom</packaging>
     
         <modules>
    @@ -33,7 +33,7 @@
     		<dependency>
     			<groupId>org.springframework.security.oauth</groupId>
     			<artifactId>spring-security-oauth2</artifactId>
    -			<version>2.1.3.BUILD-SNAPSHOT</version>
    +			<version>2.1.3.RELEASE</version>
     			<exclusions>
     				<exclusion>
     					<artifactId>jackson-mapper-asl</artifactId>
    
  • tests/xml/vanilla/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.1.3.BUILD-SNAPSHOT</version>
    +		<version>2.1.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
636e2e01fa32

Release version 2.2.3.RELEASE

34 files changed · +36 36
  • pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
       <name>OAuth for Spring Security</name>
       <description>Parent Project for OAuth Support for Spring Security</description>
       <packaging>pom</packaging>
    -  <version>2.2.3.BUILD-SNAPSHOT</version>
    +  <version>2.2.3.RELEASE</version>
       <url>http://static.springframework.org/spring-security/oauth</url>
     
       <modules>
    
  • samples/oauth2/sparklr/pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     		<relativePath>../../..</relativePath>
     	</parent>
     
    
  • samples/oauth2/tonr/pom.xml+1 1 modified
    @@ -6,7 +6,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     		<relativePath>../../..</relativePath>
     	</parent>
     
    
  • samples/oauth/sparklr/pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     		<relativePath>../../..</relativePath>
     	</parent>
     
    
  • samples/oauth/tonr/pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     		<relativePath>../../..</relativePath>
     	</parent>
     
    
  • samples/pom.xml+1 1 modified
    @@ -4,7 +4,7 @@
       <parent>
         <groupId>org.springframework.security.oauth</groupId>
         <artifactId>spring-security-oauth-parent</artifactId>
    -    <version>2.2.3.BUILD-SNAPSHOT</version>
    +    <version>2.2.3.RELEASE</version>
       </parent>
     
       <artifactId>spring-security-oauth-samples</artifactId>
    
  • spring-security-oauth2/pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<artifactId>spring-security-oauth2</artifactId>
    
  • spring-security-oauth/pom.xml+1 1 modified
    @@ -4,7 +4,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<artifactId>spring-security-oauth</artifactId>
    
  • tests/annotation/approval/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/client/pom.xml+1 1 modified
    @@ -11,7 +11,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<properties>
    
  • tests/annotation/common/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/custom-authentication/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/custom-grant/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/form/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/jaxb/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/jdbc/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/jpa/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/jwt/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/mappings/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/multi/pom.xml+1 1 modified
    @@ -9,7 +9,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/pom.xml+2 2 modified
    @@ -4,7 +4,7 @@
     
     	<groupId>org.demo</groupId>
     	<artifactId>spring-oauth2-tests-parent</artifactId>
    -	<version>2.2.3.BUILD-SNAPSHOT</version>
    +	<version>2.2.3.RELEASE</version>
         <packaging>pom</packaging>
     
         <modules>
    @@ -39,7 +39,7 @@
     		<dependency>
     			<groupId>org.springframework.security.oauth</groupId>
     			<artifactId>spring-security-oauth2</artifactId>
    -			<version>2.2.3.BUILD-SNAPSHOT</version>
    +			<version>2.2.3.RELEASE</version>
     			<exclusions>
     				<exclusion>
     					<artifactId>jackson-mapper-asl</artifactId>
    
  • tests/annotation/resource/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/ssl/pom.xml+1 1 modified
    @@ -11,7 +11,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/vanilla/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/pom.xml+1 1 modified
    @@ -4,7 +4,7 @@
       <parent>
         <groupId>org.springframework.security.oauth</groupId>
         <artifactId>spring-security-oauth-parent</artifactId>
    -    <version>2.2.3.BUILD-SNAPSHOT</version>
    +    <version>2.2.3.RELEASE</version>
       </parent>
     
       <artifactId>spring-security-oauth-tests</artifactId>
    
  • tests/xml/approval/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/client/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/common/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/form/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/jdbc/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/jwt/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/mappings/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/pom.xml+2 2 modified
    @@ -4,7 +4,7 @@
     
     	<groupId>org.demo</groupId>
     	<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -	<version>2.2.3.BUILD-SNAPSHOT</version>
    +	<version>2.2.3.RELEASE</version>
         <packaging>pom</packaging>
     
         <modules>
    @@ -33,7 +33,7 @@
     		<dependency>
     			<groupId>org.springframework.security.oauth</groupId>
     			<artifactId>spring-security-oauth2</artifactId>
    -			<version>2.2.3.BUILD-SNAPSHOT</version>
    +			<version>2.2.3.RELEASE</version>
     			<exclusions>
     				<exclusion>
     					<artifactId>jackson-mapper-asl</artifactId>
    
  • tests/xml/vanilla/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.2.3.BUILD-SNAPSHOT</version>
    +		<version>2.2.3.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
2b0b8415adca

Release version 2.3.4.RELEASE

34 files changed · +36 36
  • pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
       <name>OAuth for Spring Security</name>
       <description>Parent Project for OAuth Support for Spring Security</description>
       <packaging>pom</packaging>
    -  <version>2.3.4.BUILD-SNAPSHOT</version>
    +  <version>2.3.4.RELEASE</version>
       <url>http://static.springframework.org/spring-security/oauth</url>
     
       <modules>
    
  • samples/oauth2/sparklr/pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     		<relativePath>../../..</relativePath>
     	</parent>
     
    
  • samples/oauth2/tonr/pom.xml+1 1 modified
    @@ -6,7 +6,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     		<relativePath>../../..</relativePath>
     	</parent>
     
    
  • samples/oauth/sparklr/pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     		<relativePath>../../..</relativePath>
     	</parent>
     
    
  • samples/oauth/tonr/pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     		<relativePath>../../..</relativePath>
     	</parent>
     
    
  • samples/pom.xml+1 1 modified
    @@ -4,7 +4,7 @@
       <parent>
         <groupId>org.springframework.security.oauth</groupId>
         <artifactId>spring-security-oauth-parent</artifactId>
    -    <version>2.3.4.BUILD-SNAPSHOT</version>
    +    <version>2.3.4.RELEASE</version>
       </parent>
     
       <artifactId>spring-security-oauth-samples</artifactId>
    
  • spring-security-oauth2/pom.xml+1 1 modified
    @@ -5,7 +5,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<artifactId>spring-security-oauth2</artifactId>
    
  • spring-security-oauth/pom.xml+1 1 modified
    @@ -4,7 +4,7 @@
     	<parent>
     		<groupId>org.springframework.security.oauth</groupId>
     		<artifactId>spring-security-oauth-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<artifactId>spring-security-oauth</artifactId>
    
  • tests/annotation/approval/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/client/pom.xml+1 1 modified
    @@ -11,7 +11,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<properties>
    
  • tests/annotation/common/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/custom-authentication/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/custom-grant/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/form/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/jaxb/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/jdbc/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/jpa/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/jwt/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/mappings/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/multi/pom.xml+1 1 modified
    @@ -9,7 +9,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/pom.xml+2 2 modified
    @@ -4,7 +4,7 @@
     
     	<groupId>org.demo</groupId>
     	<artifactId>spring-oauth2-tests-parent</artifactId>
    -	<version>2.3.4.BUILD-SNAPSHOT</version>
    +	<version>2.3.4.RELEASE</version>
         <packaging>pom</packaging>
     
         <modules>
    @@ -39,7 +39,7 @@
     		<dependency>
     			<groupId>org.springframework.security.oauth</groupId>
     			<artifactId>spring-security-oauth2</artifactId>
    -			<version>2.3.4.BUILD-SNAPSHOT</version>
    +			<version>2.3.4.RELEASE</version>
     			<exclusions>
     				<exclusion>
     					<artifactId>jackson-mapper-asl</artifactId>
    
  • tests/annotation/resource/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/ssl/pom.xml+1 1 modified
    @@ -11,7 +11,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/annotation/vanilla/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/pom.xml+1 1 modified
    @@ -4,7 +4,7 @@
       <parent>
         <groupId>org.springframework.security.oauth</groupId>
         <artifactId>spring-security-oauth-parent</artifactId>
    -    <version>2.3.4.BUILD-SNAPSHOT</version>
    +    <version>2.3.4.RELEASE</version>
       </parent>
     
       <artifactId>spring-security-oauth-tests</artifactId>
    
  • tests/xml/approval/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/client/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/common/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/form/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/jdbc/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/jwt/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/mappings/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
  • tests/xml/pom.xml+2 2 modified
    @@ -4,7 +4,7 @@
     
     	<groupId>org.demo</groupId>
     	<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -	<version>2.3.4.BUILD-SNAPSHOT</version>
    +	<version>2.3.4.RELEASE</version>
         <packaging>pom</packaging>
     
         <modules>
    @@ -33,7 +33,7 @@
     		<dependency>
     			<groupId>org.springframework.security.oauth</groupId>
     			<artifactId>spring-security-oauth2</artifactId>
    -			<version>2.3.4.BUILD-SNAPSHOT</version>
    +			<version>2.3.4.RELEASE</version>
     			<exclusions>
     				<exclusion>
     					<artifactId>jackson-mapper-asl</artifactId>
    
  • tests/xml/vanilla/pom.xml+1 1 modified
    @@ -10,7 +10,7 @@
     	<parent>
     		<groupId>org.demo</groupId>
     		<artifactId>spring-oauth2-tests-xml-parent</artifactId>
    -		<version>2.3.4.BUILD-SNAPSHOT</version>
    +		<version>2.3.4.RELEASE</version>
     	</parent>
     
     	<dependencies>
    
f92223afc716

Validate authorization request on approval

2 files changed · +237 47
  • spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/AuthorizationEndpoint.java+103 18 modified
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2002-2011 the original author or authors.
    + * Copyright 2002-2018 the original author or authors.
      * 
      * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
      * the License. You may obtain a copy of the License at
    @@ -10,23 +10,14 @@
      * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
      * specific language governing permissions and limitations under the License.
      */
    -
     package org.springframework.security.oauth2.provider.endpoint;
     
    -import java.net.URI;
    -import java.security.Principal;
    -import java.util.Collections;
    -import java.util.Date;
    -import java.util.HashMap;
    -import java.util.LinkedHashMap;
    -import java.util.Map;
    -import java.util.Set;
    -
     import org.springframework.http.ResponseEntity;
     import org.springframework.security.access.AccessDeniedException;
     import org.springframework.security.authentication.InsufficientAuthenticationException;
     import org.springframework.security.core.Authentication;
     import org.springframework.security.core.AuthenticationException;
    +import org.springframework.security.core.GrantedAuthority;
     import org.springframework.security.oauth2.common.OAuth2AccessToken;
     import org.springframework.security.oauth2.common.exceptions.BadClientCredentialsException;
     import org.springframework.security.oauth2.common.exceptions.ClientAuthenticationException;
    @@ -51,6 +42,7 @@
     import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
     import org.springframework.security.oauth2.provider.implicit.ImplicitTokenRequest;
     import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
    +import org.springframework.util.ObjectUtils;
     import org.springframework.util.StringUtils;
     import org.springframework.web.HttpSessionRequiredException;
     import org.springframework.web.bind.annotation.ExceptionHandler;
    @@ -68,6 +60,16 @@
     import org.springframework.web.util.UriComponents;
     import org.springframework.web.util.UriComponentsBuilder;
     
    +import java.net.URI;
    +import java.security.Principal;
    +import java.util.Collections;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.HashSet;
    +import java.util.LinkedHashMap;
    +import java.util.Map;
    +import java.util.Set;
    +
     /**
      * <p>
      * Implementation of the Authorization Endpoint from the OAuth2 specification. Accepts authorization requests, and
    @@ -86,8 +88,11 @@
      * 
      */
     @FrameworkEndpoint
    -@SessionAttributes("authorizationRequest")
    +@SessionAttributes({AuthorizationEndpoint.AUTHORIZATION_REQUEST_ATTR_NAME, AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME})
     public class AuthorizationEndpoint extends AbstractEndpoint {
    +	static final String AUTHORIZATION_REQUEST_ATTR_NAME = "authorizationRequest";
    +
    +	static final String ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME = "org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST";
     
     	private AuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
     
    @@ -174,10 +179,10 @@ public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<Strin
     				}
     			}
     
    -			// Place auth request into the model so that it is stored in the session
    -			// for approveOrDeny to use. That way we make sure that auth request comes from the session,
    -			// so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session.
    -			model.put("authorizationRequest", authorizationRequest);
    +			// Store authorizationRequest AND an immutable Map of authorizationRequest in session
    +			// which will be used to validate against in approveOrDeny()
    +			model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +			model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, unmodifiableMap(authorizationRequest));
     
     			return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
     
    @@ -189,6 +194,33 @@ public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<Strin
     
     	}
     
    +	Map<String, Object> unmodifiableMap(AuthorizationRequest authorizationRequest) {
    +		Map<String, Object> authorizationRequestMap = new HashMap<String, Object>();
    +
    +		authorizationRequestMap.put(OAuth2Utils.CLIENT_ID, authorizationRequest.getClientId());
    +		authorizationRequestMap.put(OAuth2Utils.STATE, authorizationRequest.getState());
    +		authorizationRequestMap.put(OAuth2Utils.REDIRECT_URI, authorizationRequest.getRedirectUri());
    +		if (authorizationRequest.getResponseTypes() != null) {
    +			authorizationRequestMap.put(OAuth2Utils.RESPONSE_TYPE,
    +					Collections.unmodifiableSet(new HashSet<String>(authorizationRequest.getResponseTypes())));
    +		}
    +		if (authorizationRequest.getScope() != null) {
    +			authorizationRequestMap.put(OAuth2Utils.SCOPE,
    +					Collections.unmodifiableSet(new HashSet<String>(authorizationRequest.getScope())));
    +		}
    +		authorizationRequestMap.put("approved", authorizationRequest.isApproved());
    +		if (authorizationRequest.getResourceIds() != null) {
    +			authorizationRequestMap.put("resourceIds",
    +					Collections.unmodifiableSet(new HashSet<String>(authorizationRequest.getResourceIds())));
    +		}
    +		if (authorizationRequest.getAuthorities() != null) {
    +			authorizationRequestMap.put("authorities",
    +					Collections.unmodifiableSet(new HashSet<GrantedAuthority>(authorizationRequest.getAuthorities())));
    +		}
    +
    +		return Collections.unmodifiableMap(authorizationRequestMap);
    +	}
    +
     	@RequestMapping(value = "/oauth/authorize", method = RequestMethod.POST, params = OAuth2Utils.USER_OAUTH_APPROVAL)
     	public View approveOrDeny(@RequestParam Map<String, String> approvalParameters, Map<String, ?> model,
     			SessionStatus sessionStatus, Principal principal) {
    @@ -199,13 +231,20 @@ public View approveOrDeny(@RequestParam Map<String, String> approvalParameters,
     					"User must be authenticated with Spring Security before authorizing an access token.");
     		}
     
    -		AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get("authorizationRequest");
    +		AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get(AUTHORIZATION_REQUEST_ATTR_NAME);
     
     		if (authorizationRequest == null) {
     			sessionStatus.setComplete();
     			throw new InvalidRequestException("Cannot approve uninitialized authorization request.");
     		}
     
    +		// Check to ensure the Authorization Request was not modified during the user approval step
    +		@SuppressWarnings("unchecked")
    +		Map<String, Object> originalAuthorizationRequest = (Map<String, Object>) model.get(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME);
    +		if (isAuthorizationRequestModified(authorizationRequest, originalAuthorizationRequest)) {
    +			throw new InvalidRequestException("Changes were detected from the original authorization request.");
    +		}
    +
     		try {
     			Set<String> responseTypes = authorizationRequest.getResponseTypes();
     
    @@ -238,6 +277,52 @@ public View approveOrDeny(@RequestParam Map<String, String> approvalParameters,
     
     	}
     
    +	private boolean isAuthorizationRequestModified(
    +			AuthorizationRequest authorizationRequest, Map<String, Object> originalAuthorizationRequest) {
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getClientId(),
    +				originalAuthorizationRequest.get(OAuth2Utils.CLIENT_ID))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getState(),
    +				originalAuthorizationRequest.get(OAuth2Utils.STATE))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getRedirectUri(),
    +				originalAuthorizationRequest.get(OAuth2Utils.REDIRECT_URI))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getResponseTypes(),
    +				originalAuthorizationRequest.get(OAuth2Utils.RESPONSE_TYPE))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getScope(),
    +				originalAuthorizationRequest.get(OAuth2Utils.SCOPE))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.isApproved(),
    +				originalAuthorizationRequest.get("approved"))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getResourceIds(),
    +				originalAuthorizationRequest.get("resourceIds"))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getAuthorities(),
    +				originalAuthorizationRequest.get("authorities"))) {
    +			return true;
    +		}
    +
    +		return false;
    +	}
    +
     	// We need explicit approval from the user.
     	private ModelAndView getUserApprovalPageResponse(Map<String, Object> model,
     			AuthorizationRequest authorizationRequest, Authentication principal) {
    @@ -529,7 +614,7 @@ private AuthorizationRequest getAuthorizationRequestForError(ServletWebRequest w
     
     		// If it's already there then we are in the approveOrDeny phase and we can use the saved request
     		AuthorizationRequest authorizationRequest = (AuthorizationRequest) sessionAttributeStore.retrieveAttribute(
    -				webRequest, "authorizationRequest");
    +				webRequest, AUTHORIZATION_REQUEST_ATTR_NAME);
     		if (authorizationRequest != null) {
     			return authorizationRequest;
     		}
    
  • spring-security-oauth2/src/test/java/org/springframework/security/oauth2/provider/endpoint/AuthorizationEndpointTests.java+134 29 modified
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2006-2011 the original author or authors.
    + * Copyright 2006-2018 the original author or authors.
      * 
      * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
      * the License. You may obtain a copy of the License at
    @@ -16,6 +16,8 @@
     import static org.junit.Assert.assertFalse;
     import static org.junit.Assert.assertNull;
     import static org.junit.Assert.assertTrue;
    +import static org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.AUTHORIZATION_REQUEST_ATTR_NAME;
    +import static org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME;
     
     import java.util.Arrays;
     import java.util.Collections;
    @@ -29,6 +31,7 @@
     import org.junit.Test;
     import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
     import org.springframework.security.core.Authentication;
    +import org.springframework.security.core.authority.AuthorityUtils;
     import org.springframework.security.core.authority.SimpleGrantedAuthority;
     import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
     import org.springframework.security.oauth2.common.OAuth2AccessToken;
    @@ -152,8 +155,9 @@ public void testStartAuthorizationCodeFlowForClientCredentialsFails() throws Exc
     	@Test
     	public void testAuthorizationCodeWithFragment() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put("authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com#bar", null, null, Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com#bar", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com?code=thecode#bar", ((RedirectView) result).getUrl());
    @@ -162,9 +166,9 @@ public void testAuthorizationCodeWithFragment() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com?foo=bar", null, null, Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com?foo=bar", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com?foo=bar&code=thecode", ((RedirectView) result).getUrl());
    @@ -173,8 +177,9 @@ public void testAuthorizationCodeWithQueryParams() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithTrickyState() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put("authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com", " =?s", null, Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com", " =?s", null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com?code=thecode&state=%20%3D?s", ((RedirectView) result).getUrl());
    @@ -183,10 +188,10 @@ public void testAuthorizationCodeWithTrickyState() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithMultipleQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com?foo=bar&bar=foo", null, null,
    -						Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com?foo=bar&bar=foo", null, null,
    +				Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com?foo=bar&bar=foo&code=thecode", ((RedirectView) result).getUrl());
    @@ -195,10 +200,10 @@ public void testAuthorizationCodeWithMultipleQueryParams() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithTrickyQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com?foo=b =&bar=f $", null, null,
    -						Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com?foo=b =&bar=f $", null, null,
    +				Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		String url = ((RedirectView) result).getUrl();
    @@ -211,10 +216,10 @@ public void testAuthorizationCodeWithTrickyQueryParams() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithTrickyEncodedQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com/path?foo=b%20%3D&bar=f%20$", null, null,
    -						Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest(
    +				"foo", "http://anywhere.com/path?foo=b%20%3D&bar=f%20$", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com/path?foo=b%20%3D&bar=f%20$&code=thecode", ((RedirectView) result).getUrl());
    @@ -223,10 +228,10 @@ public void testAuthorizationCodeWithTrickyEncodedQueryParams() throws Exception
     	@Test
     	public void testAuthorizationCodeWithMoreTrickyEncodedQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere?t=a%3Db%26ep%3Dtest%2540test.me", null, null,
    -						Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest(
    +				"foo", "http://anywhere?t=a%3Db%26ep%3Dtest%2540test.me", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere?t=a%3Db%26ep%3Dtest%2540test.me&code=thecode", ((RedirectView) result).getUrl());
    @@ -508,15 +513,17 @@ public void testApproveOrDeny() throws Exception {
     		request.setApproved(true);
     		Map<String, String> approvalParameters = new HashMap<String, String>();
     		approvalParameters.put("user_oauth_approval", "true");
    -		model.put("authorizationRequest", request);
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
     		assertTrue("Wrong view: " + result, ((RedirectView) result).getUrl().startsWith("http://anywhere.com"));
     	}
     
     	@Test
     	public void testApprovalDenied() throws Exception {
    -		model.put("authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com", null, null, Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		Map<String, String> approvalParameters = new HashMap<String, String>();
     		approvalParameters.put("user_oauth_approval", "false");
     		View result = endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    @@ -541,7 +548,7 @@ public void testRedirectUriOptionalForAuthorization() throws Exception {
     						.getRequestParameters(), sessionStatus, principal);
     		// RedirectUri parameter should be null (SECOAUTH-333), however the resolvedRedirectUri not
     		AuthorizationRequest authorizationRequest = (AuthorizationRequest) result.getModelMap().get(
    -				"authorizationRequest");
    +				AUTHORIZATION_REQUEST_ATTR_NAME);
     		assertNull(authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI));
     		assertEquals("http://anywhere.com", authorizationRequest.getRedirectUri());
     	}
    @@ -556,9 +563,107 @@ public void testApproveOrDenyWithOAuth2RequestWithoutRedirectUri() throws Except
     		request.setApproved(true);
     		Map<String, String> approvalParameters = new HashMap<String, String>();
     		approvalParameters.put("user_oauth_approval", "true");
    -		model.put("authorizationRequest", request);
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedClientId() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setClientId("bar");		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedState() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setState("state-5678");		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedRedirectUri() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setRedirectUri("http://somewhere.com");		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedResponseTypes() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setResponseTypes(Collections.singleton("implicit"));		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
     		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
     
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedScope() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setScope(Arrays.asList("read", "write"));		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedApproved() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		authorizationRequest.setApproved(false);
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setApproved(true);		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedResourceIds() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setResourceIds(Collections.singleton("resource-other"));		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedAuthorities() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("authority-other"));		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
     	}
     
     	private class StubAuthorizationCodeServices implements AuthorizationCodeServices {
    
4082ec7ae3d3

Validate authorization request on approval

2 files changed · +237 47
  • spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/AuthorizationEndpoint.java+103 18 modified
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2002-2011 the original author or authors.
    + * Copyright 2002-2018 the original author or authors.
      * 
      * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
      * the License. You may obtain a copy of the License at
    @@ -10,23 +10,14 @@
      * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
      * specific language governing permissions and limitations under the License.
      */
    -
     package org.springframework.security.oauth2.provider.endpoint;
     
    -import java.net.URI;
    -import java.security.Principal;
    -import java.util.Collections;
    -import java.util.Date;
    -import java.util.HashMap;
    -import java.util.LinkedHashMap;
    -import java.util.Map;
    -import java.util.Set;
    -
     import org.springframework.http.ResponseEntity;
     import org.springframework.security.access.AccessDeniedException;
     import org.springframework.security.authentication.InsufficientAuthenticationException;
     import org.springframework.security.core.Authentication;
     import org.springframework.security.core.AuthenticationException;
    +import org.springframework.security.core.GrantedAuthority;
     import org.springframework.security.oauth2.common.OAuth2AccessToken;
     import org.springframework.security.oauth2.common.exceptions.BadClientCredentialsException;
     import org.springframework.security.oauth2.common.exceptions.ClientAuthenticationException;
    @@ -51,6 +42,7 @@
     import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
     import org.springframework.security.oauth2.provider.implicit.ImplicitTokenRequest;
     import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
    +import org.springframework.util.ObjectUtils;
     import org.springframework.util.StringUtils;
     import org.springframework.web.HttpSessionRequiredException;
     import org.springframework.web.bind.annotation.ExceptionHandler;
    @@ -68,6 +60,16 @@
     import org.springframework.web.util.UriComponents;
     import org.springframework.web.util.UriComponentsBuilder;
     
    +import java.net.URI;
    +import java.security.Principal;
    +import java.util.Collections;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.HashSet;
    +import java.util.LinkedHashMap;
    +import java.util.Map;
    +import java.util.Set;
    +
     /**
      * <p>
      * Implementation of the Authorization Endpoint from the OAuth2 specification. Accepts authorization requests, and
    @@ -86,8 +88,11 @@
      * 
      */
     @FrameworkEndpoint
    -@SessionAttributes("authorizationRequest")
    +@SessionAttributes({AuthorizationEndpoint.AUTHORIZATION_REQUEST_ATTR_NAME, AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME})
     public class AuthorizationEndpoint extends AbstractEndpoint {
    +	static final String AUTHORIZATION_REQUEST_ATTR_NAME = "authorizationRequest";
    +
    +	static final String ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME = "org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST";
     
     	private AuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
     
    @@ -174,10 +179,10 @@ public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<Strin
     				}
     			}
     
    -			// Place auth request into the model so that it is stored in the session
    -			// for approveOrDeny to use. That way we make sure that auth request comes from the session,
    -			// so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session.
    -			model.put("authorizationRequest", authorizationRequest);
    +			// Store authorizationRequest AND an immutable Map of authorizationRequest in session
    +			// which will be used to validate against in approveOrDeny()
    +			model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +			model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, unmodifiableMap(authorizationRequest));
     
     			return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
     
    @@ -189,6 +194,33 @@ public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<Strin
     
     	}
     
    +	Map<String, Object> unmodifiableMap(AuthorizationRequest authorizationRequest) {
    +		Map<String, Object> authorizationRequestMap = new HashMap<String, Object>();
    +
    +		authorizationRequestMap.put(OAuth2Utils.CLIENT_ID, authorizationRequest.getClientId());
    +		authorizationRequestMap.put(OAuth2Utils.STATE, authorizationRequest.getState());
    +		authorizationRequestMap.put(OAuth2Utils.REDIRECT_URI, authorizationRequest.getRedirectUri());
    +		if (authorizationRequest.getResponseTypes() != null) {
    +			authorizationRequestMap.put(OAuth2Utils.RESPONSE_TYPE,
    +					Collections.unmodifiableSet(new HashSet<String>(authorizationRequest.getResponseTypes())));
    +		}
    +		if (authorizationRequest.getScope() != null) {
    +			authorizationRequestMap.put(OAuth2Utils.SCOPE,
    +					Collections.unmodifiableSet(new HashSet<String>(authorizationRequest.getScope())));
    +		}
    +		authorizationRequestMap.put("approved", authorizationRequest.isApproved());
    +		if (authorizationRequest.getResourceIds() != null) {
    +			authorizationRequestMap.put("resourceIds",
    +					Collections.unmodifiableSet(new HashSet<String>(authorizationRequest.getResourceIds())));
    +		}
    +		if (authorizationRequest.getAuthorities() != null) {
    +			authorizationRequestMap.put("authorities",
    +					Collections.unmodifiableSet(new HashSet<GrantedAuthority>(authorizationRequest.getAuthorities())));
    +		}
    +
    +		return Collections.unmodifiableMap(authorizationRequestMap);
    +	}
    +
     	@RequestMapping(value = "/oauth/authorize", method = RequestMethod.POST, params = OAuth2Utils.USER_OAUTH_APPROVAL)
     	public View approveOrDeny(@RequestParam Map<String, String> approvalParameters, Map<String, ?> model,
     			SessionStatus sessionStatus, Principal principal) {
    @@ -199,13 +231,20 @@ public View approveOrDeny(@RequestParam Map<String, String> approvalParameters,
     					"User must be authenticated with Spring Security before authorizing an access token.");
     		}
     
    -		AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get("authorizationRequest");
    +		AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get(AUTHORIZATION_REQUEST_ATTR_NAME);
     
     		if (authorizationRequest == null) {
     			sessionStatus.setComplete();
     			throw new InvalidRequestException("Cannot approve uninitialized authorization request.");
     		}
     
    +		// Check to ensure the Authorization Request was not modified during the user approval step
    +		@SuppressWarnings("unchecked")
    +		Map<String, Object> originalAuthorizationRequest = (Map<String, Object>) model.get(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME);
    +		if (isAuthorizationRequestModified(authorizationRequest, originalAuthorizationRequest)) {
    +			throw new InvalidRequestException("Changes were detected from the original authorization request.");
    +		}
    +
     		try {
     			Set<String> responseTypes = authorizationRequest.getResponseTypes();
     
    @@ -238,6 +277,52 @@ public View approveOrDeny(@RequestParam Map<String, String> approvalParameters,
     
     	}
     
    +	private boolean isAuthorizationRequestModified(
    +			AuthorizationRequest authorizationRequest, Map<String, Object> originalAuthorizationRequest) {
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getClientId(),
    +				originalAuthorizationRequest.get(OAuth2Utils.CLIENT_ID))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getState(),
    +				originalAuthorizationRequest.get(OAuth2Utils.STATE))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getRedirectUri(),
    +				originalAuthorizationRequest.get(OAuth2Utils.REDIRECT_URI))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getResponseTypes(),
    +				originalAuthorizationRequest.get(OAuth2Utils.RESPONSE_TYPE))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getScope(),
    +				originalAuthorizationRequest.get(OAuth2Utils.SCOPE))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.isApproved(),
    +				originalAuthorizationRequest.get("approved"))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getResourceIds(),
    +				originalAuthorizationRequest.get("resourceIds"))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getAuthorities(),
    +				originalAuthorizationRequest.get("authorities"))) {
    +			return true;
    +		}
    +
    +		return false;
    +	}
    +
     	// We need explicit approval from the user.
     	private ModelAndView getUserApprovalPageResponse(Map<String, Object> model,
     			AuthorizationRequest authorizationRequest, Authentication principal) {
    @@ -531,7 +616,7 @@ private AuthorizationRequest getAuthorizationRequestForError(ServletWebRequest w
     
     		// If it's already there then we are in the approveOrDeny phase and we can use the saved request
     		AuthorizationRequest authorizationRequest = (AuthorizationRequest) sessionAttributeStore.retrieveAttribute(
    -				webRequest, "authorizationRequest");
    +				webRequest, AUTHORIZATION_REQUEST_ATTR_NAME);
     		if (authorizationRequest != null) {
     			return authorizationRequest;
     		}
    
  • spring-security-oauth2/src/test/java/org/springframework/security/oauth2/provider/endpoint/AuthorizationEndpointTests.java+134 29 modified
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2006-2011 the original author or authors.
    + * Copyright 2006-2018 the original author or authors.
      * 
      * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
      * the License. You may obtain a copy of the License at
    @@ -16,6 +16,8 @@
     import static org.junit.Assert.assertFalse;
     import static org.junit.Assert.assertNull;
     import static org.junit.Assert.assertTrue;
    +import static org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.AUTHORIZATION_REQUEST_ATTR_NAME;
    +import static org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME;
     
     import java.util.Arrays;
     import java.util.Collections;
    @@ -29,6 +31,7 @@
     import org.junit.Test;
     import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
     import org.springframework.security.core.Authentication;
    +import org.springframework.security.core.authority.AuthorityUtils;
     import org.springframework.security.core.authority.SimpleGrantedAuthority;
     import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
     import org.springframework.security.oauth2.common.OAuth2AccessToken;
    @@ -152,8 +155,9 @@ public void testStartAuthorizationCodeFlowForClientCredentialsFails() throws Exc
     	@Test
     	public void testAuthorizationCodeWithFragment() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put("authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com#bar", null, null, Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com#bar", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com?code=thecode#bar", ((RedirectView) result).getUrl());
    @@ -162,9 +166,9 @@ public void testAuthorizationCodeWithFragment() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com?foo=bar", null, null, Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com?foo=bar", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com?foo=bar&code=thecode", ((RedirectView) result).getUrl());
    @@ -173,8 +177,9 @@ public void testAuthorizationCodeWithQueryParams() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithTrickyState() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put("authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com", " =?s", null, Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com", " =?s", null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com?code=thecode&state=%20%3D?s", ((RedirectView) result).getUrl());
    @@ -183,10 +188,10 @@ public void testAuthorizationCodeWithTrickyState() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithMultipleQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com?foo=bar&bar=foo", null, null,
    -						Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com?foo=bar&bar=foo", null, null,
    +				Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com?foo=bar&bar=foo&code=thecode", ((RedirectView) result).getUrl());
    @@ -195,10 +200,10 @@ public void testAuthorizationCodeWithMultipleQueryParams() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithTrickyQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com?foo=b =&bar=f $", null, null,
    -						Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com?foo=b =&bar=f $", null, null,
    +				Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		String url = ((RedirectView) result).getUrl();
    @@ -211,10 +216,10 @@ public void testAuthorizationCodeWithTrickyQueryParams() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithTrickyEncodedQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com/path?foo=b%20%3D&bar=f%20$", null, null,
    -						Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest(
    +				"foo", "http://anywhere.com/path?foo=b%20%3D&bar=f%20$", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com/path?foo=b%20%3D&bar=f%20$&code=thecode", ((RedirectView) result).getUrl());
    @@ -223,10 +228,10 @@ public void testAuthorizationCodeWithTrickyEncodedQueryParams() throws Exception
     	@Test
     	public void testAuthorizationCodeWithMoreTrickyEncodedQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere?t=a%3Db%26ep%3Dtest%2540test.me", null, null,
    -						Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest(
    +				"foo", "http://anywhere?t=a%3Db%26ep%3Dtest%2540test.me", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere?t=a%3Db%26ep%3Dtest%2540test.me&code=thecode", ((RedirectView) result).getUrl());
    @@ -508,15 +513,17 @@ public void testApproveOrDeny() throws Exception {
     		request.setApproved(true);
     		Map<String, String> approvalParameters = new HashMap<String, String>();
     		approvalParameters.put("user_oauth_approval", "true");
    -		model.put("authorizationRequest", request);
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
     		assertTrue("Wrong view: " + result, ((RedirectView) result).getUrl().startsWith("http://anywhere.com"));
     	}
     
     	@Test
     	public void testApprovalDenied() throws Exception {
    -		model.put("authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com", null, null, Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		Map<String, String> approvalParameters = new HashMap<String, String>();
     		approvalParameters.put("user_oauth_approval", "false");
     		View result = endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    @@ -541,7 +548,7 @@ public void testRedirectUriOptionalForAuthorization() throws Exception {
     						.getRequestParameters(), sessionStatus, principal);
     		// RedirectUri parameter should be null (SECOAUTH-333), however the resolvedRedirectUri not
     		AuthorizationRequest authorizationRequest = (AuthorizationRequest) result.getModelMap().get(
    -				"authorizationRequest");
    +				AUTHORIZATION_REQUEST_ATTR_NAME);
     		assertNull(authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI));
     		assertEquals("http://anywhere.com", authorizationRequest.getRedirectUri());
     	}
    @@ -556,9 +563,107 @@ public void testApproveOrDenyWithOAuth2RequestWithoutRedirectUri() throws Except
     		request.setApproved(true);
     		Map<String, String> approvalParameters = new HashMap<String, String>();
     		approvalParameters.put("user_oauth_approval", "true");
    -		model.put("authorizationRequest", request);
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedClientId() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setClientId("bar");		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedState() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setState("state-5678");		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedRedirectUri() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setRedirectUri("http://somewhere.com");		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedResponseTypes() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setResponseTypes(Collections.singleton("implicit"));		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
     		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
     
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedScope() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setScope(Arrays.asList("read", "write"));		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedApproved() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		authorizationRequest.setApproved(false);
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setApproved(true);		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedResourceIds() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setResourceIds(Collections.singleton("resource-other"));		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedAuthorities() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("authority-other"));		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
     	}
     
     	private class StubAuthorizationCodeServices implements AuthorizationCodeServices {
    
ddd65cd9417a

Validate authorization request on approval

2 files changed · +237 47
  • spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/AuthorizationEndpoint.java+103 18 modified
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2002-2011 the original author or authors.
    + * Copyright 2002-2018 the original author or authors.
      * 
      * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
      * the License. You may obtain a copy of the License at
    @@ -10,23 +10,14 @@
      * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
      * specific language governing permissions and limitations under the License.
      */
    -
     package org.springframework.security.oauth2.provider.endpoint;
     
    -import java.net.URI;
    -import java.security.Principal;
    -import java.util.Collections;
    -import java.util.Date;
    -import java.util.HashMap;
    -import java.util.LinkedHashMap;
    -import java.util.Map;
    -import java.util.Set;
    -
     import org.springframework.http.ResponseEntity;
     import org.springframework.security.access.AccessDeniedException;
     import org.springframework.security.authentication.InsufficientAuthenticationException;
     import org.springframework.security.core.Authentication;
     import org.springframework.security.core.AuthenticationException;
    +import org.springframework.security.core.GrantedAuthority;
     import org.springframework.security.oauth2.common.OAuth2AccessToken;
     import org.springframework.security.oauth2.common.exceptions.BadClientCredentialsException;
     import org.springframework.security.oauth2.common.exceptions.ClientAuthenticationException;
    @@ -51,6 +42,7 @@
     import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
     import org.springframework.security.oauth2.provider.implicit.ImplicitTokenRequest;
     import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
    +import org.springframework.util.ObjectUtils;
     import org.springframework.util.StringUtils;
     import org.springframework.web.HttpSessionRequiredException;
     import org.springframework.web.bind.annotation.ExceptionHandler;
    @@ -68,6 +60,16 @@
     import org.springframework.web.util.UriComponents;
     import org.springframework.web.util.UriComponentsBuilder;
     
    +import java.net.URI;
    +import java.security.Principal;
    +import java.util.Collections;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.HashSet;
    +import java.util.LinkedHashMap;
    +import java.util.Map;
    +import java.util.Set;
    +
     /**
      * <p>
      * Implementation of the Authorization Endpoint from the OAuth2 specification. Accepts authorization requests, and
    @@ -86,8 +88,11 @@
      * 
      */
     @FrameworkEndpoint
    -@SessionAttributes("authorizationRequest")
    +@SessionAttributes({AuthorizationEndpoint.AUTHORIZATION_REQUEST_ATTR_NAME, AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME})
     public class AuthorizationEndpoint extends AbstractEndpoint {
    +	static final String AUTHORIZATION_REQUEST_ATTR_NAME = "authorizationRequest";
    +
    +	static final String ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME = "org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST";
     
     	private AuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
     
    @@ -174,10 +179,10 @@ public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<Strin
     				}
     			}
     
    -			// Place auth request into the model so that it is stored in the session
    -			// for approveOrDeny to use. That way we make sure that auth request comes from the session,
    -			// so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session.
    -			model.put("authorizationRequest", authorizationRequest);
    +			// Store authorizationRequest AND an immutable Map of authorizationRequest in session
    +			// which will be used to validate against in approveOrDeny()
    +			model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +			model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, unmodifiableMap(authorizationRequest));
     
     			return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
     
    @@ -189,6 +194,33 @@ public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<Strin
     
     	}
     
    +	Map<String, Object> unmodifiableMap(AuthorizationRequest authorizationRequest) {
    +		Map<String, Object> authorizationRequestMap = new HashMap<String, Object>();
    +
    +		authorizationRequestMap.put(OAuth2Utils.CLIENT_ID, authorizationRequest.getClientId());
    +		authorizationRequestMap.put(OAuth2Utils.STATE, authorizationRequest.getState());
    +		authorizationRequestMap.put(OAuth2Utils.REDIRECT_URI, authorizationRequest.getRedirectUri());
    +		if (authorizationRequest.getResponseTypes() != null) {
    +			authorizationRequestMap.put(OAuth2Utils.RESPONSE_TYPE,
    +					Collections.unmodifiableSet(new HashSet<String>(authorizationRequest.getResponseTypes())));
    +		}
    +		if (authorizationRequest.getScope() != null) {
    +			authorizationRequestMap.put(OAuth2Utils.SCOPE,
    +					Collections.unmodifiableSet(new HashSet<String>(authorizationRequest.getScope())));
    +		}
    +		authorizationRequestMap.put("approved", authorizationRequest.isApproved());
    +		if (authorizationRequest.getResourceIds() != null) {
    +			authorizationRequestMap.put("resourceIds",
    +					Collections.unmodifiableSet(new HashSet<String>(authorizationRequest.getResourceIds())));
    +		}
    +		if (authorizationRequest.getAuthorities() != null) {
    +			authorizationRequestMap.put("authorities",
    +					Collections.unmodifiableSet(new HashSet<GrantedAuthority>(authorizationRequest.getAuthorities())));
    +		}
    +
    +		return Collections.unmodifiableMap(authorizationRequestMap);
    +	}
    +
     	@RequestMapping(value = "/oauth/authorize", method = RequestMethod.POST, params = OAuth2Utils.USER_OAUTH_APPROVAL)
     	public View approveOrDeny(@RequestParam Map<String, String> approvalParameters, Map<String, ?> model,
     			SessionStatus sessionStatus, Principal principal) {
    @@ -199,13 +231,20 @@ public View approveOrDeny(@RequestParam Map<String, String> approvalParameters,
     					"User must be authenticated with Spring Security before authorizing an access token.");
     		}
     
    -		AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get("authorizationRequest");
    +		AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get(AUTHORIZATION_REQUEST_ATTR_NAME);
     
     		if (authorizationRequest == null) {
     			sessionStatus.setComplete();
     			throw new InvalidRequestException("Cannot approve uninitialized authorization request.");
     		}
     
    +		// Check to ensure the Authorization Request was not modified during the user approval step
    +		@SuppressWarnings("unchecked")
    +		Map<String, Object> originalAuthorizationRequest = (Map<String, Object>) model.get(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME);
    +		if (isAuthorizationRequestModified(authorizationRequest, originalAuthorizationRequest)) {
    +			throw new InvalidRequestException("Changes were detected from the original authorization request.");
    +		}
    +
     		try {
     			Set<String> responseTypes = authorizationRequest.getResponseTypes();
     
    @@ -238,6 +277,52 @@ public View approveOrDeny(@RequestParam Map<String, String> approvalParameters,
     
     	}
     
    +	private boolean isAuthorizationRequestModified(
    +			AuthorizationRequest authorizationRequest, Map<String, Object> originalAuthorizationRequest) {
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getClientId(),
    +				originalAuthorizationRequest.get(OAuth2Utils.CLIENT_ID))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getState(),
    +				originalAuthorizationRequest.get(OAuth2Utils.STATE))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getRedirectUri(),
    +				originalAuthorizationRequest.get(OAuth2Utils.REDIRECT_URI))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getResponseTypes(),
    +				originalAuthorizationRequest.get(OAuth2Utils.RESPONSE_TYPE))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getScope(),
    +				originalAuthorizationRequest.get(OAuth2Utils.SCOPE))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.isApproved(),
    +				originalAuthorizationRequest.get("approved"))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getResourceIds(),
    +				originalAuthorizationRequest.get("resourceIds"))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getAuthorities(),
    +				originalAuthorizationRequest.get("authorities"))) {
    +			return true;
    +		}
    +
    +		return false;
    +	}
    +
     	// We need explicit approval from the user.
     	private ModelAndView getUserApprovalPageResponse(Map<String, Object> model,
     			AuthorizationRequest authorizationRequest, Authentication principal) {
    @@ -529,7 +614,7 @@ private AuthorizationRequest getAuthorizationRequestForError(ServletWebRequest w
     
     		// If it's already there then we are in the approveOrDeny phase and we can use the saved request
     		AuthorizationRequest authorizationRequest = (AuthorizationRequest) sessionAttributeStore.retrieveAttribute(
    -				webRequest, "authorizationRequest");
    +				webRequest, AUTHORIZATION_REQUEST_ATTR_NAME);
     		if (authorizationRequest != null) {
     			return authorizationRequest;
     		}
    
  • spring-security-oauth2/src/test/java/org/springframework/security/oauth2/provider/endpoint/AuthorizationEndpointTests.java+134 29 modified
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2006-2011 the original author or authors.
    + * Copyright 2006-2018 the original author or authors.
      * 
      * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
      * the License. You may obtain a copy of the License at
    @@ -16,6 +16,8 @@
     import static org.junit.Assert.assertFalse;
     import static org.junit.Assert.assertNull;
     import static org.junit.Assert.assertTrue;
    +import static org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.AUTHORIZATION_REQUEST_ATTR_NAME;
    +import static org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME;
     
     import java.util.Arrays;
     import java.util.Collections;
    @@ -29,6 +31,7 @@
     import org.junit.Test;
     import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
     import org.springframework.security.core.Authentication;
    +import org.springframework.security.core.authority.AuthorityUtils;
     import org.springframework.security.core.authority.SimpleGrantedAuthority;
     import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
     import org.springframework.security.oauth2.common.OAuth2AccessToken;
    @@ -152,8 +155,9 @@ public void testStartAuthorizationCodeFlowForClientCredentialsFails() throws Exc
     	@Test
     	public void testAuthorizationCodeWithFragment() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put("authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com#bar", null, null, Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com#bar", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com?code=thecode#bar", ((RedirectView) result).getUrl());
    @@ -162,9 +166,9 @@ public void testAuthorizationCodeWithFragment() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com?foo=bar", null, null, Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com?foo=bar", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com?foo=bar&code=thecode", ((RedirectView) result).getUrl());
    @@ -173,8 +177,9 @@ public void testAuthorizationCodeWithQueryParams() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithTrickyState() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put("authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com", " =?s", null, Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com", " =?s", null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com?code=thecode&state=%20%3D?s", ((RedirectView) result).getUrl());
    @@ -183,10 +188,10 @@ public void testAuthorizationCodeWithTrickyState() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithMultipleQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com?foo=bar&bar=foo", null, null,
    -						Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com?foo=bar&bar=foo", null, null,
    +				Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com?foo=bar&bar=foo&code=thecode", ((RedirectView) result).getUrl());
    @@ -195,10 +200,10 @@ public void testAuthorizationCodeWithMultipleQueryParams() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithTrickyQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com?foo=b =&bar=f $", null, null,
    -						Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com?foo=b =&bar=f $", null, null,
    +				Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		String url = ((RedirectView) result).getUrl();
    @@ -211,10 +216,10 @@ public void testAuthorizationCodeWithTrickyQueryParams() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithTrickyEncodedQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com/path?foo=b%20%3D&bar=f%20$", null, null,
    -						Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest(
    +				"foo", "http://anywhere.com/path?foo=b%20%3D&bar=f%20$", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com/path?foo=b%20%3D&bar=f%20$&code=thecode", ((RedirectView) result).getUrl());
    @@ -223,10 +228,10 @@ public void testAuthorizationCodeWithTrickyEncodedQueryParams() throws Exception
     	@Test
     	public void testAuthorizationCodeWithMoreTrickyEncodedQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere?t=a%3Db%26ep%3Dtest%2540test.me", null, null,
    -						Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest(
    +				"foo", "http://anywhere?t=a%3Db%26ep%3Dtest%2540test.me", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere?t=a%3Db%26ep%3Dtest%2540test.me&code=thecode", ((RedirectView) result).getUrl());
    @@ -508,15 +513,17 @@ public void testApproveOrDeny() throws Exception {
     		request.setApproved(true);
     		Map<String, String> approvalParameters = new HashMap<String, String>();
     		approvalParameters.put("user_oauth_approval", "true");
    -		model.put("authorizationRequest", request);
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
     		assertTrue("Wrong view: " + result, ((RedirectView) result).getUrl().startsWith("http://anywhere.com"));
     	}
     
     	@Test
     	public void testApprovalDenied() throws Exception {
    -		model.put("authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com", null, null, Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		Map<String, String> approvalParameters = new HashMap<String, String>();
     		approvalParameters.put("user_oauth_approval", "false");
     		View result = endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    @@ -541,7 +548,7 @@ public void testRedirectUriOptionalForAuthorization() throws Exception {
     						.getRequestParameters(), sessionStatus, principal);
     		// RedirectUri parameter should be null (SECOAUTH-333), however the resolvedRedirectUri not
     		AuthorizationRequest authorizationRequest = (AuthorizationRequest) result.getModelMap().get(
    -				"authorizationRequest");
    +				AUTHORIZATION_REQUEST_ATTR_NAME);
     		assertNull(authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI));
     		assertEquals("http://anywhere.com", authorizationRequest.getRedirectUri());
     	}
    @@ -556,9 +563,107 @@ public void testApproveOrDenyWithOAuth2RequestWithoutRedirectUri() throws Except
     		request.setApproved(true);
     		Map<String, String> approvalParameters = new HashMap<String, String>();
     		approvalParameters.put("user_oauth_approval", "true");
    -		model.put("authorizationRequest", request);
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedClientId() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setClientId("bar");		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedState() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setState("state-5678");		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedRedirectUri() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setRedirectUri("http://somewhere.com");		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedResponseTypes() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setResponseTypes(Collections.singleton("implicit"));		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
     		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
     
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedScope() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setScope(Arrays.asList("read", "write"));		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedApproved() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		authorizationRequest.setApproved(false);
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setApproved(true);		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedResourceIds() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setResourceIds(Collections.singleton("resource-other"));		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedAuthorities() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("authority-other"));		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
     	}
     
     	private class StubAuthorizationCodeServices implements AuthorizationCodeServices {
    
623776689fdc

Validate authorization request on approval

2 files changed · +237 47
  • spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/AuthorizationEndpoint.java+103 18 modified
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2002-2011 the original author or authors.
    + * Copyright 2002-2018 the original author or authors.
      * 
      * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
      * the License. You may obtain a copy of the License at
    @@ -10,23 +10,14 @@
      * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
      * specific language governing permissions and limitations under the License.
      */
    -
     package org.springframework.security.oauth2.provider.endpoint;
     
    -import java.net.URI;
    -import java.security.Principal;
    -import java.util.Collections;
    -import java.util.Date;
    -import java.util.HashMap;
    -import java.util.LinkedHashMap;
    -import java.util.Map;
    -import java.util.Set;
    -
     import org.springframework.http.ResponseEntity;
     import org.springframework.security.access.AccessDeniedException;
     import org.springframework.security.authentication.InsufficientAuthenticationException;
     import org.springframework.security.core.Authentication;
     import org.springframework.security.core.AuthenticationException;
    +import org.springframework.security.core.GrantedAuthority;
     import org.springframework.security.oauth2.common.OAuth2AccessToken;
     import org.springframework.security.oauth2.common.exceptions.BadClientCredentialsException;
     import org.springframework.security.oauth2.common.exceptions.ClientAuthenticationException;
    @@ -51,6 +42,7 @@
     import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
     import org.springframework.security.oauth2.provider.implicit.ImplicitTokenRequest;
     import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
    +import org.springframework.util.ObjectUtils;
     import org.springframework.util.StringUtils;
     import org.springframework.web.HttpSessionRequiredException;
     import org.springframework.web.bind.annotation.ExceptionHandler;
    @@ -68,6 +60,16 @@
     import org.springframework.web.util.UriComponents;
     import org.springframework.web.util.UriComponentsBuilder;
     
    +import java.net.URI;
    +import java.security.Principal;
    +import java.util.Collections;
    +import java.util.Date;
    +import java.util.HashMap;
    +import java.util.HashSet;
    +import java.util.LinkedHashMap;
    +import java.util.Map;
    +import java.util.Set;
    +
     /**
      * <p>
      * Implementation of the Authorization Endpoint from the OAuth2 specification. Accepts authorization requests, and
    @@ -86,8 +88,11 @@
      * 
      */
     @FrameworkEndpoint
    -@SessionAttributes("authorizationRequest")
    +@SessionAttributes({AuthorizationEndpoint.AUTHORIZATION_REQUEST_ATTR_NAME, AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME})
     public class AuthorizationEndpoint extends AbstractEndpoint {
    +	static final String AUTHORIZATION_REQUEST_ATTR_NAME = "authorizationRequest";
    +
    +	static final String ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME = "org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST";
     
     	private AuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
     
    @@ -174,10 +179,10 @@ public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<Strin
     				}
     			}
     
    -			// Place auth request into the model so that it is stored in the session
    -			// for approveOrDeny to use. That way we make sure that auth request comes from the session,
    -			// so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session.
    -			model.put("authorizationRequest", authorizationRequest);
    +			// Store authorizationRequest AND an immutable Map of authorizationRequest in session
    +			// which will be used to validate against in approveOrDeny()
    +			model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +			model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, unmodifiableMap(authorizationRequest));
     
     			return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
     
    @@ -189,6 +194,33 @@ public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<Strin
     
     	}
     
    +	Map<String, Object> unmodifiableMap(AuthorizationRequest authorizationRequest) {
    +		Map<String, Object> authorizationRequestMap = new HashMap<String, Object>();
    +
    +		authorizationRequestMap.put(OAuth2Utils.CLIENT_ID, authorizationRequest.getClientId());
    +		authorizationRequestMap.put(OAuth2Utils.STATE, authorizationRequest.getState());
    +		authorizationRequestMap.put(OAuth2Utils.REDIRECT_URI, authorizationRequest.getRedirectUri());
    +		if (authorizationRequest.getResponseTypes() != null) {
    +			authorizationRequestMap.put(OAuth2Utils.RESPONSE_TYPE,
    +					Collections.unmodifiableSet(new HashSet<String>(authorizationRequest.getResponseTypes())));
    +		}
    +		if (authorizationRequest.getScope() != null) {
    +			authorizationRequestMap.put(OAuth2Utils.SCOPE,
    +					Collections.unmodifiableSet(new HashSet<String>(authorizationRequest.getScope())));
    +		}
    +		authorizationRequestMap.put("approved", authorizationRequest.isApproved());
    +		if (authorizationRequest.getResourceIds() != null) {
    +			authorizationRequestMap.put("resourceIds",
    +					Collections.unmodifiableSet(new HashSet<String>(authorizationRequest.getResourceIds())));
    +		}
    +		if (authorizationRequest.getAuthorities() != null) {
    +			authorizationRequestMap.put("authorities",
    +					Collections.unmodifiableSet(new HashSet<GrantedAuthority>(authorizationRequest.getAuthorities())));
    +		}
    +
    +		return Collections.unmodifiableMap(authorizationRequestMap);
    +	}
    +
     	@RequestMapping(value = "/oauth/authorize", method = RequestMethod.POST, params = OAuth2Utils.USER_OAUTH_APPROVAL)
     	public View approveOrDeny(@RequestParam Map<String, String> approvalParameters, Map<String, ?> model,
     			SessionStatus sessionStatus, Principal principal) {
    @@ -199,13 +231,20 @@ public View approveOrDeny(@RequestParam Map<String, String> approvalParameters,
     					"User must be authenticated with Spring Security before authorizing an access token.");
     		}
     
    -		AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get("authorizationRequest");
    +		AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get(AUTHORIZATION_REQUEST_ATTR_NAME);
     
     		if (authorizationRequest == null) {
     			sessionStatus.setComplete();
     			throw new InvalidRequestException("Cannot approve uninitialized authorization request.");
     		}
     
    +		// Check to ensure the Authorization Request was not modified during the user approval step
    +		@SuppressWarnings("unchecked")
    +		Map<String, Object> originalAuthorizationRequest = (Map<String, Object>) model.get(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME);
    +		if (isAuthorizationRequestModified(authorizationRequest, originalAuthorizationRequest)) {
    +			throw new InvalidRequestException("Changes were detected from the original authorization request.");
    +		}
    +
     		try {
     			Set<String> responseTypes = authorizationRequest.getResponseTypes();
     
    @@ -238,6 +277,52 @@ public View approveOrDeny(@RequestParam Map<String, String> approvalParameters,
     
     	}
     
    +	private boolean isAuthorizationRequestModified(
    +			AuthorizationRequest authorizationRequest, Map<String, Object> originalAuthorizationRequest) {
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getClientId(),
    +				originalAuthorizationRequest.get(OAuth2Utils.CLIENT_ID))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getState(),
    +				originalAuthorizationRequest.get(OAuth2Utils.STATE))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getRedirectUri(),
    +				originalAuthorizationRequest.get(OAuth2Utils.REDIRECT_URI))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getResponseTypes(),
    +				originalAuthorizationRequest.get(OAuth2Utils.RESPONSE_TYPE))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getScope(),
    +				originalAuthorizationRequest.get(OAuth2Utils.SCOPE))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.isApproved(),
    +				originalAuthorizationRequest.get("approved"))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getResourceIds(),
    +				originalAuthorizationRequest.get("resourceIds"))) {
    +			return true;
    +		}
    +		if (!ObjectUtils.nullSafeEquals(
    +				authorizationRequest.getAuthorities(),
    +				originalAuthorizationRequest.get("authorities"))) {
    +			return true;
    +		}
    +
    +		return false;
    +	}
    +
     	// We need explicit approval from the user.
     	private ModelAndView getUserApprovalPageResponse(Map<String, Object> model,
     			AuthorizationRequest authorizationRequest, Authentication principal) {
    @@ -529,7 +614,7 @@ private AuthorizationRequest getAuthorizationRequestForError(ServletWebRequest w
     
     		// If it's already there then we are in the approveOrDeny phase and we can use the saved request
     		AuthorizationRequest authorizationRequest = (AuthorizationRequest) sessionAttributeStore.retrieveAttribute(
    -				webRequest, "authorizationRequest");
    +				webRequest, AUTHORIZATION_REQUEST_ATTR_NAME);
     		if (authorizationRequest != null) {
     			return authorizationRequest;
     		}
    
  • spring-security-oauth2/src/test/java/org/springframework/security/oauth2/provider/endpoint/AuthorizationEndpointTests.java+134 29 modified
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2006-2011 the original author or authors.
    + * Copyright 2006-2018 the original author or authors.
      * 
      * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
      * the License. You may obtain a copy of the License at
    @@ -16,6 +16,8 @@
     import static org.junit.Assert.assertFalse;
     import static org.junit.Assert.assertNull;
     import static org.junit.Assert.assertTrue;
    +import static org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.AUTHORIZATION_REQUEST_ATTR_NAME;
    +import static org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME;
     
     import java.util.Arrays;
     import java.util.Collections;
    @@ -29,6 +31,7 @@
     import org.junit.Test;
     import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
     import org.springframework.security.core.Authentication;
    +import org.springframework.security.core.authority.AuthorityUtils;
     import org.springframework.security.core.authority.SimpleGrantedAuthority;
     import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
     import org.springframework.security.oauth2.common.OAuth2AccessToken;
    @@ -152,8 +155,9 @@ public void testStartAuthorizationCodeFlowForClientCredentialsFails() throws Exc
     	@Test
     	public void testAuthorizationCodeWithFragment() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put("authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com#bar", null, null, Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com#bar", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com?code=thecode#bar", ((RedirectView) result).getUrl());
    @@ -162,9 +166,9 @@ public void testAuthorizationCodeWithFragment() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com?foo=bar", null, null, Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com?foo=bar", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com?foo=bar&code=thecode", ((RedirectView) result).getUrl());
    @@ -173,8 +177,9 @@ public void testAuthorizationCodeWithQueryParams() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithTrickyState() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put("authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com", " =?s", null, Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com", " =?s", null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com?code=thecode&state=%20%3D?s", ((RedirectView) result).getUrl());
    @@ -183,10 +188,10 @@ public void testAuthorizationCodeWithTrickyState() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithMultipleQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com?foo=bar&bar=foo", null, null,
    -						Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com?foo=bar&bar=foo", null, null,
    +				Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com?foo=bar&bar=foo&code=thecode", ((RedirectView) result).getUrl());
    @@ -195,10 +200,10 @@ public void testAuthorizationCodeWithMultipleQueryParams() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithTrickyQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com?foo=b =&bar=f $", null, null,
    -						Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com?foo=b =&bar=f $", null, null,
    +				Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		String url = ((RedirectView) result).getUrl();
    @@ -211,10 +216,10 @@ public void testAuthorizationCodeWithTrickyQueryParams() throws Exception {
     	@Test
     	public void testAuthorizationCodeWithTrickyEncodedQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com/path?foo=b%20%3D&bar=f%20$", null, null,
    -						Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest(
    +				"foo", "http://anywhere.com/path?foo=b%20%3D&bar=f%20$", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere.com/path?foo=b%20%3D&bar=f%20$&code=thecode", ((RedirectView) result).getUrl());
    @@ -223,10 +228,10 @@ public void testAuthorizationCodeWithTrickyEncodedQueryParams() throws Exception
     	@Test
     	public void testAuthorizationCodeWithMoreTrickyEncodedQueryParams() throws Exception {
     		endpoint.setAuthorizationCodeServices(new StubAuthorizationCodeServices());
    -		model.put(
    -				"authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere?t=a%3Db%26ep%3Dtest%2540test.me", null, null,
    -						Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest(
    +				"foo", "http://anywhere?t=a%3Db%26ep%3Dtest%2540test.me", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(Collections.singletonMap(OAuth2Utils.USER_OAUTH_APPROVAL, "true"), model,
     				sessionStatus, principal);
     		assertEquals("http://anywhere?t=a%3Db%26ep%3Dtest%2540test.me&code=thecode", ((RedirectView) result).getUrl());
    @@ -508,15 +513,17 @@ public void testApproveOrDeny() throws Exception {
     		request.setApproved(true);
     		Map<String, String> approvalParameters = new HashMap<String, String>();
     		approvalParameters.put("user_oauth_approval", "true");
    -		model.put("authorizationRequest", request);
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		View result = endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
     		assertTrue("Wrong view: " + result, ((RedirectView) result).getUrl().startsWith("http://anywhere.com"));
     	}
     
     	@Test
     	public void testApprovalDenied() throws Exception {
    -		model.put("authorizationRequest",
    -				getAuthorizationRequest("foo", "http://anywhere.com", null, null, Collections.singleton("code")));
    +		AuthorizationRequest request = getAuthorizationRequest("foo", "http://anywhere.com", null, null, Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
     		Map<String, String> approvalParameters = new HashMap<String, String>();
     		approvalParameters.put("user_oauth_approval", "false");
     		View result = endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    @@ -541,7 +548,7 @@ public void testRedirectUriOptionalForAuthorization() throws Exception {
     						.getRequestParameters(), sessionStatus, principal);
     		// RedirectUri parameter should be null (SECOAUTH-333), however the resolvedRedirectUri not
     		AuthorizationRequest authorizationRequest = (AuthorizationRequest) result.getModelMap().get(
    -				"authorizationRequest");
    +				AUTHORIZATION_REQUEST_ATTR_NAME);
     		assertNull(authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI));
     		assertEquals("http://anywhere.com", authorizationRequest.getRedirectUri());
     	}
    @@ -556,9 +563,107 @@ public void testApproveOrDenyWithOAuth2RequestWithoutRedirectUri() throws Except
     		request.setApproved(true);
     		Map<String, String> approvalParameters = new HashMap<String, String>();
     		approvalParameters.put("user_oauth_approval", "true");
    -		model.put("authorizationRequest", request);
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, request);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(request));
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedClientId() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setClientId("bar");		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedState() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setState("state-5678");		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedRedirectUri() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setRedirectUri("http://somewhere.com");		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedResponseTypes() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setResponseTypes(Collections.singleton("implicit"));		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
     		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
     
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedScope() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setScope(Arrays.asList("read", "write"));		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedApproved() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		authorizationRequest.setApproved(false);
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setApproved(true);		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedResourceIds() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setResourceIds(Collections.singleton("resource-other"));		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
    +	}
    +
    +	@Test(expected = InvalidRequestException.class)
    +	public void testApproveWithModifiedAuthorities() throws Exception {
    +		AuthorizationRequest authorizationRequest = getAuthorizationRequest(
    +				"foo", "http://anywhere.com", "state-1234", "read", Collections.singleton("code"));
    +		model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest);
    +		model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, endpoint.unmodifiableMap(authorizationRequest));
    +		authorizationRequest.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("authority-other"));		// Modify authorization request
    +		Map<String, String> approvalParameters = new HashMap<String, String>();
    +		approvalParameters.put("user_oauth_approval", "true");
    +		endpoint.approveOrDeny(approvalParameters, model, sessionStatus, principal);
     	}
     
     	private class StubAuthorizationCodeServices implements AuthorizationCodeServices {
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

9

News mentions

0

No linked articles in our index yet.