Skip to content

Commit d7698fe

Browse files
committed
Added token endpoint implementation
Closes spring-projectsgh-67
1 parent e25e116 commit d7698fe

File tree

5 files changed

+350
-1
lines changed

5 files changed

+350
-1
lines changed

core/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2Authorization.java

+4
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,15 @@
2121

2222
/**
2323
* @author Joe Grandja
24+
* @author Madhu Bhat
2425
*/
2526
public class OAuth2Authorization {
2627
private String registeredClientId;
2728
private String principalName;
2829
private OAuth2AccessToken accessToken;
2930
private Map<String, Object> attributes;
3031

32+
public final void setAccessToken(OAuth2AccessToken accessToken) {
33+
this.accessToken = accessToken;
34+
}
3135
}

core/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AccessTokenAuthenticationToken.java

+5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
/**
2727
* @author Joe Grandja
28+
* @author Madhu Bhat
2829
*/
2930
public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthenticationToken {
3031
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
@@ -49,4 +50,8 @@ public Object getCredentials() {
4950
public Object getPrincipal() {
5051
return null;
5152
}
53+
54+
public OAuth2AccessToken getAccessToken() {
55+
return accessToken;
56+
}
5257
}

core/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationToken.java

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
/**
2626
* @author Joe Grandja
27+
* @author Madhu Bhat
2728
*/
2829
public class OAuth2AuthorizationCodeAuthenticationToken extends AbstractAuthenticationToken {
2930
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
@@ -57,4 +58,8 @@ public Object getCredentials() {
5758
public Object getPrincipal() {
5859
return null;
5960
}
61+
62+
public String getCode() {
63+
return code;
64+
}
6065
}

core/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java

+121-1
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,150 @@
1515
*/
1616
package org.springframework.security.oauth2.server.authorization.web;
1717

18+
import com.fasterxml.jackson.annotation.JsonInclude;
19+
import com.fasterxml.jackson.databind.ObjectMapper;
1820
import org.springframework.core.convert.converter.Converter;
21+
import org.springframework.http.HttpHeaders;
22+
import org.springframework.http.HttpMethod;
23+
import org.springframework.http.HttpStatus;
24+
import org.springframework.http.MediaType;
1925
import org.springframework.security.authentication.AuthenticationManager;
2026
import org.springframework.security.core.Authentication;
27+
import org.springframework.security.core.context.SecurityContextHolder;
28+
import org.springframework.security.oauth2.core.*;
29+
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
30+
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
2131
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
32+
import org.springframework.security.oauth2.server.authorization.TokenType;
33+
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
34+
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken;
35+
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
36+
import org.springframework.security.web.util.matcher.RequestMatcher;
37+
import org.springframework.util.Assert;
38+
import org.springframework.util.StringUtils;
2239
import org.springframework.web.filter.OncePerRequestFilter;
2340

2441
import javax.servlet.FilterChain;
2542
import javax.servlet.ServletException;
2643
import javax.servlet.http.HttpServletRequest;
2744
import javax.servlet.http.HttpServletResponse;
2845
import java.io.IOException;
46+
import java.io.Writer;
2947

3048
/**
3149
* @author Joe Grandja
50+
* @author Madhu Bhat
3251
*/
3352
public class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
34-
private Converter<HttpServletRequest, Authentication> authorizationGrantConverter;
53+
private static final String DEFAULT_TOKEN_ENDPOINT_URI = "/oauth2/token";
54+
private Converter<HttpServletRequest, Authentication> authorizationGrantConverter = this::convert;
3555
private AuthenticationManager authenticationManager;
3656
private OAuth2AuthorizationService authorizationService;
57+
private RequestMatcher uriMatcher;
58+
private ObjectMapper objectMapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
59+
60+
/**
61+
* Constructs an {@code OAuth2TokenEndpointFilter} using the provided parameters.
62+
*
63+
* @param authorizationService the authorization service implementation
64+
* @param authenticationManager the authentication manager implementation
65+
*/
66+
public OAuth2TokenEndpointFilter(OAuth2AuthorizationService authorizationService, AuthenticationManager authenticationManager) {
67+
Assert.notNull(authorizationService, "authorizationService cannot be null");
68+
Assert.notNull(authenticationManager, "authenticationManager cannot be null");
69+
this.authenticationManager = authenticationManager;
70+
this.authorizationService = authorizationService;
71+
this.uriMatcher = new AntPathRequestMatcher(DEFAULT_TOKEN_ENDPOINT_URI, HttpMethod.POST.name());
72+
}
73+
74+
/**
75+
* Constructs an {@code OAuth2TokenEndpointFilter} using the provided parameters.
76+
*
77+
* @param authorizationService the authorization service implementation
78+
* @param authenticationManager the authentication manager implementation
79+
* @param tokenEndpointUri the token endpoint's uri
80+
*/
81+
public OAuth2TokenEndpointFilter(OAuth2AuthorizationService authorizationService, AuthenticationManager authenticationManager,
82+
String tokenEndpointUri) {
83+
Assert.notNull(authorizationService, "authorizationService cannot be null");
84+
Assert.notNull(authenticationManager, "authenticationManager cannot be null");
85+
Assert.hasText(tokenEndpointUri, "tokenEndpointUri cannot be empty");
86+
this.authenticationManager = authenticationManager;
87+
this.authorizationService = authorizationService;
88+
this.uriMatcher = new AntPathRequestMatcher(tokenEndpointUri, HttpMethod.POST.name());
89+
}
3790

3891
@Override
3992
protected void doFilterInternal(HttpServletRequest request,
4093
HttpServletResponse response, FilterChain filterChain)
4194
throws ServletException, IOException {
95+
if (uriMatcher.matches(request)) {
96+
try {
97+
if (validateAccessTokenRequest(request)) {
98+
OAuth2AuthorizationCodeAuthenticationToken authCodeAuthToken =
99+
(OAuth2AuthorizationCodeAuthenticationToken) authorizationGrantConverter.convert(request);
100+
OAuth2AccessTokenAuthenticationToken accessTokenAuthenticationToken =
101+
(OAuth2AccessTokenAuthenticationToken) authenticationManager.authenticate(authCodeAuthToken);
102+
if (accessTokenAuthenticationToken.isAuthenticated()) {
103+
OAuth2Authorization authorization = authorizationService
104+
.findByTokenAndTokenType(authCodeAuthToken.getCode(), TokenType.AUTHORIZATION_CODE);
105+
authorization.setAccessToken(accessTokenAuthenticationToken.getAccessToken());
106+
authorizationService.save(authorization);
107+
writeSuccessResponse(response, accessTokenAuthenticationToken.getAccessToken());
108+
} else {
109+
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT));
110+
}
111+
}
112+
} catch (OAuth2AuthenticationException exception) {
113+
SecurityContextHolder.clearContext();
114+
writeFailureResponse(response, exception.getError());
115+
}
116+
} else {
117+
filterChain.doFilter(request, response);
118+
}
119+
}
120+
121+
private boolean validateAccessTokenRequest(HttpServletRequest request) {
122+
if (StringUtils.isEmpty(request.getParameter(OAuth2ParameterNames.CODE))
123+
|| StringUtils.isEmpty(request.getParameter(OAuth2ParameterNames.REDIRECT_URI))
124+
|| StringUtils.isEmpty(request.getParameter(OAuth2ParameterNames.GRANT_TYPE))) {
125+
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST));
126+
} else if (!AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(request.getParameter(OAuth2ParameterNames.GRANT_TYPE))) {
127+
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.UNSUPPORTED_GRANT_TYPE));
128+
}
129+
return true;
130+
}
131+
132+
private OAuth2AuthorizationCodeAuthenticationToken convert(HttpServletRequest request) {
133+
Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
134+
return new OAuth2AuthorizationCodeAuthenticationToken(
135+
request.getParameter(OAuth2ParameterNames.CODE),
136+
clientPrincipal,
137+
request.getParameter(OAuth2ParameterNames.REDIRECT_URI)
138+
);
139+
}
140+
141+
private void writeSuccessResponse(HttpServletResponse response, OAuth2AccessToken body) throws IOException {
142+
try (Writer out = response.getWriter()) {
143+
response.setStatus(HttpStatus.OK.value());
144+
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
145+
response.setCharacterEncoding("UTF-8");
146+
response.setHeader(HttpHeaders.CACHE_CONTROL, "no-store");
147+
response.setHeader(HttpHeaders.PRAGMA, "no-cache");
148+
out.write(objectMapper.writeValueAsString(body));
149+
}
150+
}
42151

152+
private void writeFailureResponse(HttpServletResponse response, OAuth2Error error) throws IOException {
153+
try (Writer out = response.getWriter()) {
154+
if (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_CLIENT)) {
155+
response.setStatus(HttpStatus.UNAUTHORIZED.value());
156+
} else {
157+
response.setStatus(HttpStatus.BAD_REQUEST.value());
158+
}
159+
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
160+
response.setCharacterEncoding("UTF-8");
161+
out.write(objectMapper.writeValueAsString(error));
162+
}
43163
}
44164
}

0 commit comments

Comments
 (0)