Is there a way to add a Login Success Handler using spring-oauth2?
I tried using Basic Authentication Filter but it only filters the client credentials not the user credentials.
Or do I need to create a custom user authentication manager?
TIA
Is there a way to add a Login Success Handler using spring-oauth2?
I tried using Basic Authentication Filter but it only filters the client credentials not the user credentials.
Or do I need to create a custom user authentication manager?
TIA
This solution will work for password flow and for others I am not sure. You can add your custom filter in position of "before=BASIC_AUTH_FILTER" in http tag which is in oauth-server config, and you can achieve by parse response of "oauth/token" so create ByteArrayResponseWrapper to get response, Here I am using TeeOutputStream class from "org.apache.commons commons-io",
private class ByteArrayResponseWrapper extends HttpServletResponseWrapper {
public ByteArrayResponseWrapper(ServletResponse response) {
super((HttpServletResponse) response);
}
private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new DelegatingServletOutputStream(new TeeOutputStream(
super.getOutputStream(), byteArrayOutputStream));
}
public byte[] getByteArray() {
return this.byteArrayOutputStream.toByteArray();
}
}
and I have created token extractor to separate the code of extracting access_token
public class OAuth2AccessTokenExtractor implements
OAuth2AccessTokenExtractor {
private ObjectMapper mapper = new ObjectMapper();
public String getAccessTokenValue(byte[] response) {
try {
return mapper.readValue(response, OAuth2AccessToken.class)
.getValue();
} catch (JsonParseException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
after create your filter override doFilter like this
private DefaultTokenServices tokenServices;
private OAuth2AccessTokenExtractor tokenExtractor;
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// create wrapper to read response body
ByteArrayResponseWrapper responseWraper = new ByteArrayResponseWrapper(
response);
// led them go
chain.doFilter(request, responseWraper);
// get ClientAuthentication
Authentication clientAuthentication = SecurityContextHolder
.getContext().getAuthentication();
// is authenticated or not to proceed
if (clientAuthentication != null
&& clientAuthentication.isAuthenticated()) {
// callBack client authenticated successfully
onSuccessfulClientAuthentication(request, response,
clientAuthentication);
// check response status is success of failure
if (responseWraper.getStatus() == 200) {
// extract accessToken from response
String token = tokenExtractor
.getAccessTokenValue(responseWraper.getByteArray());
if (token != null && !token.isEmpty()) {
// load authentication from token
OAuth2Authentication oAuth2Authentication = this.tokenServices
.loadAuthentication(token);
OAuth2AccessToken actualAccessToken = this.tokenServices
.getAccessToken(oAuth2Authentication);
// callBack user authenticated successfully
onSuccessfulUserAuthentication(request, response,
clientAuthentication, oAuth2Authentication,
actualAccessToken);
} else {
log.error("access token is empty from extractor");
}
} else {
// callBack user authenticated failure
onFailureUserAuthentication(request, response,
clientAuthentication, request.getParameter("username"));
}
} else {
// callBack client authenticated failure
onFailClientAuthentication(request, response,
request.getParameter(OAuth2Utils.CLIENT_ID));
}
}
protected void onSuccessfulClientAuthentication(ServletRequest request,
ServletResponse response, Authentication authentication) {
}
protected void onFailClientAuthentication(ServletRequest request,
ServletResponse response, String clientId) {
}
protected void onSuccessfulUserAuthentication(ServletRequest request,
ServletResponse response, Authentication clientAuthentication,
OAuth2Authentication userOAuth2Authentication,
OAuth2AccessToken token) {
}
protected void onFailureUserAuthentication(ServletRequest request,
ServletResponse response, Authentication clientAuthentication,
String username) {
}
while create filter instance inject tokenServices. now onSuccessfulClientAuthentication, onFailClientAuthentication, onSuccessfulUserAuthentication and onFailureUserAuthentication will be called according to your authentication
for more you can refer this code on github
Edited:
The above snippet works fine when you have default token response and it is just uses ServletResponseWrapper and extracting.
But still it seems like vulnerable so you can get to know the user authentication success by through org.springframework.security.oauth2.provider.token.TokenEnhancer
class
Follow this answer for details.
We built a custom authentication manager which we wired into the OAuth2AuthenticationProcessingFilter to get this done. The manager's authenticate method is able to unpack the OAuth2Authentication and OAuth2AuthenticationDetails from the authentication principal.
<bean id="oAuth2AuthenticationManager" class="org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager">
<property name="resourceId" value="XXX-api"/>
<property name="tokenServices" ref="tokenServices"/>
</bean>
<bean id="resourceServerFilter"
class="org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter">
<property name="authenticationManager" ref="oAuth2AuthenticationManager"/>
<property name="tokenExtractor">
<bean class="com.xxx.oauth.BearerTokenExtractor"/>
</property>
</bean>