Skip to content

Commit a531534

Browse files
author
Dave Syer
committed
SECOAUTH-160: allow multiple response types (but don't process them yet)
1 parent ef697d8 commit a531534

File tree

9 files changed

+162
-33
lines changed

9 files changed

+162
-33
lines changed

spring-security-oauth2/src/main/java/org/springframework/security/oauth2/http/converter/jaxb/AbstractJaxbMessageConverter.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
* @param <I>
3737
* @param <E>
3838
*/
39+
@SuppressWarnings("restriction")
3940
abstract class AbstractJaxbMessageConverter<I, E> extends AbstractXmlHttpMessageConverter<E> {
4041

4142
private final Class<I> internalClass;

spring-security-oauth2/src/main/java/org/springframework/security/oauth2/http/converter/jaxb/JaxbOAuth2AccessToken.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import javax.xml.bind.annotation.XmlRootElement;
1919
import javax.xml.bind.annotation.XmlTransient;
2020

21+
@SuppressWarnings("restriction")
2122
@XmlRootElement(name = "oauth")
2223
class JaxbOAuth2AccessToken {
2324
private String accessToken;

spring-security-oauth2/src/main/java/org/springframework/security/oauth2/http/converter/jaxb/JaxbOAuth2Exception.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import javax.xml.bind.annotation.XmlElement;
1818
import javax.xml.bind.annotation.XmlRootElement;
1919

20+
@SuppressWarnings("restriction")
2021
@XmlRootElement(name = "oauth")
2122
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
2223
class JaxbOAuth2Exception {

spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/AuthorizationEndpoint.java

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
package org.springframework.security.oauth2.provider.endpoint;
1515

1616
import java.security.Principal;
17+
import java.util.Arrays;
1718
import java.util.Collections;
1819
import java.util.Date;
20+
import java.util.HashSet;
1921
import java.util.Map;
2022
import java.util.Set;
2123

@@ -40,12 +42,14 @@
4042
import org.springframework.security.oauth2.provider.code.UserApprovalHandler;
4143
import org.springframework.stereotype.Controller;
4244
import org.springframework.util.Assert;
45+
import org.springframework.util.StringUtils;
4346
import org.springframework.web.bind.annotation.ModelAttribute;
4447
import org.springframework.web.bind.annotation.RequestMapping;
4548
import org.springframework.web.bind.annotation.RequestMethod;
4649
import org.springframework.web.bind.annotation.RequestParam;
4750
import org.springframework.web.bind.annotation.SessionAttributes;
4851
import org.springframework.web.bind.support.SessionStatus;
52+
import org.springframework.web.servlet.ModelAndView;
4953
import org.springframework.web.servlet.View;
5054
import org.springframework.web.servlet.view.RedirectView;
5155

@@ -55,6 +59,7 @@
5559
*/
5660
@Controller
5761
@SessionAttributes(types = UnconfirmedAuthorizationCodeClientToken.class)
62+
@RequestMapping(value = "/oauth/authorize")
5863
public class AuthorizationEndpoint extends AbstractEndpoint implements InitializingBean {
5964

6065
public static final String USER_OAUTH_APPROVAL = "user_oauth_approval";
@@ -77,7 +82,8 @@ public void afterPropertiesSet() throws Exception {
7782
}
7883

7984
@ModelAttribute
80-
public UnconfirmedAuthorizationCodeClientToken getClientToken(@RequestParam(value = "client_id", required = false) String clientId,
85+
public UnconfirmedAuthorizationCodeClientToken getClientToken(
86+
@RequestParam(value = "client_id", required = false) String clientId,
8187
@RequestParam(value = "redirect_uri", required = false) String redirectUri,
8288
@RequestParam(value = "state", required = false) String state,
8389
@RequestParam(value = "scope", required = false) String scopes) {
@@ -89,9 +95,11 @@ public UnconfirmedAuthorizationCodeClientToken getClientToken(@RequestParam(valu
8995
}
9096

9197
// if the "response_type" is "code", we can process this request.
92-
@RequestMapping(value = "/oauth/authorize", params = "response_type=code", method = RequestMethod.GET)
93-
public String startAuthorization(Map<String, Object> model, @RequestParam Map<String, String> parameters,
94-
UnconfirmedAuthorizationCodeClientToken authToken, SessionStatus sessionStatus, Principal principal) {
98+
@RequestMapping(params="response_type")
99+
public ModelAndView authorize(Map<String, Object> model, @RequestParam("response_type") String responseType,
100+
@RequestParam Map<String, String> parameters,
101+
@ModelAttribute UnconfirmedAuthorizationCodeClientToken authToken, SessionStatus sessionStatus,
102+
Principal principal) {
95103

96104
if (authToken.getClientId() == null) {
97105
sessionStatus.setComplete();
@@ -104,6 +112,24 @@ public String startAuthorization(Map<String, Object> model, @RequestParam Map<St
104112
"User must be authenticated with Spring Security before forwarding to user approval page.");
105113
}
106114

115+
Set<String> responseTypes = new HashSet<String>(Arrays.asList(StringUtils.delimitedListToStringArray(
116+
responseType, " ")));
117+
118+
if (responseTypes.contains("code")) {
119+
return new ModelAndView(startAuthorization(model, parameters), model);
120+
}
121+
122+
if (responseTypes.contains("token")) {
123+
return new ModelAndView(implicitAuthorization(authToken, sessionStatus));
124+
}
125+
126+
throw new UnsupportedResponseTypeException("Unsupported response type: " + responseType);
127+
128+
}
129+
130+
// if the "response_type" is "code", we can process this request.
131+
private String startAuthorization(Map<String, Object> model, Map<String, String> parameters) {
132+
107133
logger.debug("Loading user approval page: " + userApprovalPage);
108134
// In case of a redirect we might want the request parameters to be included
109135
model.putAll(parameters);
@@ -112,23 +138,7 @@ public String startAuthorization(Map<String, Object> model, @RequestParam Map<St
112138
}
113139

114140
// if the "response_type" is "token", we can process this request.
115-
@RequestMapping(value = "/oauth/authorize", params = "response_type=token")
116-
public View implicitAuthorization(UnconfirmedAuthorizationCodeClientToken authToken, SessionStatus sessionStatus,
117-
Principal principal) {
118-
119-
if (authToken.getClientId() == null) {
120-
sessionStatus.setComplete();
121-
throw new InvalidClientException("A client_id must be supplied.");
122-
}
123-
else {
124-
authToken.setDenied(false);
125-
}
126-
127-
if (!(principal instanceof Authentication)) {
128-
sessionStatus.setComplete();
129-
throw new InsufficientAuthenticationException(
130-
"User must be authenticated with Spring Security before implicitly granting an access token.");
131-
}
141+
private View implicitAuthorization(UnconfirmedAuthorizationCodeClientToken authToken, SessionStatus sessionStatus) {
132142

133143
try {
134144
String requestedRedirect = redirectResolver.resolveRedirect(authToken.getRequestedRedirect(),
@@ -147,14 +157,10 @@ Collections.<String, String> emptyMap(), authToken.getClientId(), authToken.getC
147157

148158
}
149159

150-
@RequestMapping(value = "/oauth/authorize", method = RequestMethod.GET)
151-
public String rejectAuthorization(@RequestParam("response_type") String responseType) {
152-
throw new UnsupportedResponseTypeException("Unsupported response type: " + responseType);
153-
}
154-
155-
@RequestMapping(value = "/oauth/authorize", method = RequestMethod.POST)
160+
@RequestMapping(method = RequestMethod.POST)
156161
public View approveOrDeny(@RequestParam(USER_OAUTH_APPROVAL) boolean approved,
157-
UnconfirmedAuthorizationCodeClientToken authToken, SessionStatus sessionStatus, Principal principal) {
162+
@ModelAttribute UnconfirmedAuthorizationCodeClientToken authToken, SessionStatus sessionStatus,
163+
Principal principal) {
158164

159165
if (authToken.getClientId() == null) {
160166
sessionStatus.setComplete();

spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/filter/ClientCredentialsTokenEndpointFilter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,13 @@
2222
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
2323
import org.springframework.security.core.Authentication;
2424
import org.springframework.security.core.AuthenticationException;
25-
import org.springframework.security.oauth2.web.authentication.OAuth2AuthenticationFailureHandler;
2625
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
2726
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
2827

2928
/**
3029
* A filter and authentication endpoint for the OAuth2 Token Endpoint. Allows clients to authenticate using request
3130
* parameters if included as a security filter, as permitted by the specification (but not recommended). It is
32-
* recommended by the specification that you permit HTTP basic authentication for cllients, and not use this filter at
31+
* recommended by the specification that you permit HTTP basic authentication for clients, and not use this filter at
3332
* all.
3433
*
3534
* @author Dave Syer
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
1111
* specific language governing permissions and limitations under the License.
1212
*/
13-
package org.springframework.security.oauth2.web.authentication;
13+
package org.springframework.security.oauth2.provider.filter;
1414

1515
import java.io.IOException;
1616

spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/token/RandomValueTokenServices.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
* Persistence is delegated to a {@code TokenStore} implementation.
3939
*
4040
* @author Ryan Heaton
41+
* @author Luke Taylor
42+
* @author Dave Syer
4143
*/
4244
public class RandomValueTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices,
4345
InitializingBean {
@@ -97,7 +99,9 @@ else if (isExpired(refreshToken)) {
9799
refreshToken = createRefreshToken(authentication);
98100
}
99101

100-
return createAccessToken(authentication, refreshToken);
102+
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
103+
tokenStore.storeAccessToken(accessToken, authentication);
104+
return accessToken;
101105
}
102106

103107
/**
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright 2006-2011 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
package org.springframework.security.oauth2.provider.endpoint;
14+
15+
import static org.junit.Assert.assertEquals;
16+
import static org.junit.Assert.assertTrue;
17+
18+
import java.util.Collections;
19+
import java.util.HashMap;
20+
import java.util.Map;
21+
import java.util.Set;
22+
23+
import org.junit.Test;
24+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
25+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
26+
import org.springframework.security.oauth2.common.OAuth2AccessToken;
27+
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
28+
import org.springframework.security.oauth2.provider.BaseClientDetails;
29+
import org.springframework.security.oauth2.provider.ClientDetails;
30+
import org.springframework.security.oauth2.provider.ClientDetailsService;
31+
import org.springframework.security.oauth2.provider.TokenGranter;
32+
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
33+
import org.springframework.security.oauth2.provider.code.UnconfirmedAuthorizationCodeClientToken;
34+
import org.springframework.web.bind.support.SimpleSessionStatus;
35+
import org.springframework.web.servlet.ModelAndView;
36+
import org.springframework.web.servlet.View;
37+
import org.springframework.web.servlet.view.RedirectView;
38+
39+
/**
40+
* @author Dave Syer
41+
*
42+
*/
43+
public class TestAuthorizationEndpoint {
44+
45+
private AuthorizationEndpoint endpoint = new AuthorizationEndpoint();
46+
47+
private HashMap<String, Object> model = new HashMap<String, Object>();
48+
49+
private HashMap<String, String> parameters = new HashMap<String, String>();
50+
51+
private SimpleSessionStatus sessionStatus = new SimpleSessionStatus();
52+
53+
private UsernamePasswordAuthenticationToken principal = new UsernamePasswordAuthenticationToken("foo", "bar",
54+
Collections.singleton(new SimpleGrantedAuthority("ROLE_USER")));
55+
56+
@Test(expected = IllegalStateException.class)
57+
public void testMandatoryProperties() throws Exception {
58+
endpoint.afterPropertiesSet();
59+
}
60+
61+
@Test
62+
public void testGetClientToken() {
63+
UnconfirmedAuthorizationCodeClientToken clientToken = endpoint.getClientToken("foo", "http://anywhere.com",
64+
"bar", "baz");
65+
assertEquals("bar", clientToken.getState());
66+
assertEquals("foo", clientToken.getClientId());
67+
assertEquals("http://anywhere.com", clientToken.getRequestedRedirect());
68+
assertEquals("[baz]", clientToken.getScope().toString());
69+
}
70+
71+
@Test
72+
public void testAuthorizationCode() {
73+
ModelAndView result = endpoint.authorize(model, "code", parameters,
74+
endpoint.getClientToken("foo", null, null, null), sessionStatus, principal);
75+
assertEquals("forward:/oauth/confirm_access", result.getViewName());
76+
}
77+
78+
@Test
79+
public void testAuthorizationCodeWithMultipleResponseTypes() {
80+
ModelAndView result = endpoint.authorize(model, "code other", parameters,
81+
endpoint.getClientToken("foo", null, null, null), sessionStatus, principal);
82+
assertEquals("forward:/oauth/confirm_access", result.getViewName());
83+
}
84+
85+
@Test
86+
public void testImplicit() {
87+
endpoint.setTokenGranter(new TokenGranter() {
88+
public OAuth2AccessToken grant(String grantType, Map<String, String> parameters, String clientId,
89+
String clientSecret, Set<String> scope) {
90+
return null;
91+
}
92+
});
93+
endpoint.setClientDetailsService(new ClientDetailsService() {
94+
public ClientDetails loadClientByClientId(String clientId) throws OAuth2Exception {
95+
return new BaseClientDetails();
96+
}
97+
});
98+
ModelAndView result = endpoint.authorize(model, "token", parameters,
99+
endpoint.getClientToken("foo", "http://anywhere.com", null, null), sessionStatus, principal);
100+
assertTrue("Wrong view: " + result, ((RedirectView) result.getView()).getUrl()
101+
.startsWith("http://anywhere.com"));
102+
}
103+
104+
@Test
105+
public void testApproveOrDeny() {
106+
endpoint.setClientDetailsService(new ClientDetailsService() {
107+
public ClientDetails loadClientByClientId(String clientId) throws OAuth2Exception {
108+
return new BaseClientDetails();
109+
}
110+
});
111+
endpoint.setAuthorizationCodeServices(new InMemoryAuthorizationCodeServices());
112+
View result = endpoint.approveOrDeny(true, endpoint.getClientToken("foo", "http://anywhere.com", null, null),
113+
sessionStatus, principal);
114+
assertTrue("Wrong view: " + result, ((RedirectView) result).getUrl().startsWith("http://anywhere.com"));
115+
}
116+
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
1111
* specific language governing permissions and limitations under the License.
1212
*/
13-
package org.springframework.security.oauth2.web.authentication;
13+
package org.springframework.security.oauth2.provider.filter;
1414

1515
import static org.junit.Assert.assertEquals;
1616
import static org.junit.Assert.assertSame;
@@ -32,6 +32,7 @@
3232
import org.springframework.security.core.AuthenticationException;
3333
import org.springframework.security.core.userdetails.UsernameNotFoundException;
3434
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
35+
import org.springframework.security.oauth2.provider.filter.OAuth2AuthenticationFailureHandler;
3536
import org.springframework.security.oauth2.provider.web.OAuth2ExceptionRenderer;
3637
import org.springframework.web.context.request.ServletWebRequest;
3738

0 commit comments

Comments
 (0)