Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce login form for OAuth 2.0 login provider (common/oauth2-provider) #48

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package de.helfenkannjeder.come2help.server.configuration.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import de.helfenkannjeder.come2help.server.security.jwt.FacebookSuccessHandler;
import de.helfenkannjeder.come2help.server.security.jwt.GoogleSuccessHandler;
import de.helfenkannjeder.come2help.server.security.jwt.HelfenkannjederSuccessHandler;
import de.helfenkannjeder.come2help.server.security.jwt.StatelessJwtAuthenticationFilter;
import java.util.Arrays;
import javax.servlet.Filter;
import de.helfenkannjeder.come2help.server.security.oauth2.CustomOAuthAuthenticationProcessingFilter;
import de.helfenkannjeder.come2help.server.security.oauth2.token.AccessTokenService;
import de.helfenkannjeder.come2help.server.security.oauth2.token.AuthorizationCodeAccessTokenService;
import de.helfenkannjeder.come2help.server.security.oauth2.token.PasswordAccessTokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
Expand All @@ -13,19 +17,27 @@
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.web.filter.CompositeFilter;

import javax.servlet.Filter;
import java.util.Arrays;

@Configuration
@EnableOAuth2Client
public class OAuth2ClientConfigurer extends WebSecurityConfigurerAdapter {

@Autowired
private MappingJackson2HttpMessageConverter jsonMessageConverter;

@Autowired
private ObjectMapper objectMapper;

/**
* Configure HttpSecurity. This includes:<br>
* - resources requiring authorized <br>
Expand All @@ -38,17 +50,25 @@ public class OAuth2ClientConfigurer extends WebSecurityConfigurerAdapter {
*/
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// @formatter:off
httpSecurity
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.csrf().disable().headers().frameOptions().disable().and()
.antMatcher("/**").authorizeRequests()
.antMatchers("/login/**").permitAll()
.antMatchers("/abilities/**").permitAll()
.antMatchers("/jsondoc/**").permitAll()
.antMatchers("/jsondoc-ui.html").permitAll()
.antMatchers("/webjars/jsondoc-ui-webjar/**").permitAll()
.anyRequest().authenticated().and()
.exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint()).and();
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf() .disable()
.headers().frameOptions().disable()
.and()
.antMatcher("/**").authorizeRequests()
.antMatchers("/login/**").permitAll()
.antMatchers("/abilities/**").permitAll()
.antMatchers("/jsondoc/**").permitAll()
.antMatchers("/jsondoc-ui.html").permitAll()
.antMatchers("/webjars/jsondoc-ui-webjar/**").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(new Http403ForbiddenEntryPoint());
// @formatter:on

httpSecurity.addFilterBefore(statelessJwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
httpSecurity.addFilterBefore(createOAuth2Filter(), BasicAuthenticationFilter.class);
Expand All @@ -57,18 +77,26 @@ protected void configure(HttpSecurity httpSecurity) throws Exception {
private Filter createOAuth2Filter() {
CompositeFilter filter = new CompositeFilter();
filter.setFilters(Arrays.asList(
createOAuth2Filter(facebook(), facebookSuccessHandler(), "/login/facebook"),
createOAuth2Filter(google(), googleSuccessHandler(), "/login/google"))
createOAuth2Filter(facebook(), facebookSuccessHandler(), "/login/facebook", getAuthorizationCodeAccessTokenService()),
createOAuth2Filter(google(), googleSuccessHandler(), "/login/google", getAuthorizationCodeAccessTokenService()),
createOAuth2Filter(helfenkannjeder(), helfenkannjederSuccessHandler(), "/login/helfenkannjeder", new PasswordAccessTokenService(objectMapper))
)
);
return filter;
}

private AbstractAuthenticationProcessingFilter createOAuth2Filter(ClientResourceDetails clientDetails, AuthenticationSuccessHandler successHandler, String path) {
CustomOAuthAuthenticationProcessingFilter oauthFilter = new CustomOAuthAuthenticationProcessingFilter(path, clientDetails, jsonMessageConverter);
private AbstractAuthenticationProcessingFilter createOAuth2Filter(ClientResourceDetails clientDetails, AuthenticationSuccessHandler successHandler, String path, AccessTokenService accessTokenService) {
CustomOAuthAuthenticationProcessingFilter oauthFilter = new CustomOAuthAuthenticationProcessingFilter(path, clientDetails,
accessTokenService);
accessTokenService.setClientResourceDetails(clientDetails);
oauthFilter.setAuthenticationSuccessHandler(successHandler);
return oauthFilter;
}

private AccessTokenService getAuthorizationCodeAccessTokenService() {
return new AuthorizationCodeAccessTokenService(jsonMessageConverter);
}

@Bean
protected StatelessJwtAuthenticationFilter statelessJwtAuthenticationFilter() {
return new StatelessJwtAuthenticationFilter();
Expand All @@ -86,6 +114,12 @@ protected ClientResourceDetails google() {
return new ClientResourceDetails();
}

@Bean
@ConfigurationProperties("helfenkannjeder")
protected ClientResourceDetails helfenkannjeder() {
return new ClientResourceDetails();
}

@Bean
protected AuthenticationSuccessHandler facebookSuccessHandler() {
return new FacebookSuccessHandler();
Expand All @@ -95,4 +129,9 @@ protected AuthenticationSuccessHandler facebookSuccessHandler() {
protected AuthenticationSuccessHandler googleSuccessHandler() {
return new GoogleSuccessHandler();
}

@Bean
protected AuthenticationSuccessHandler helfenkannjederSuccessHandler() {
return new HelfenkannjederSuccessHandler();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package de.helfenkannjeder.come2help.server.security.jwt;

import org.springframework.stereotype.Component;

@Component
public class HelfenkannjederSuccessHandler extends JwtCreatingAuthenticationSuccessHandler {

@Override
protected String providerIdentifier() {
return "google";
}

@Override
protected String surnameField() {
return "family_name";
}

@Override
protected String givenNameField() {
return "given_name";
}

@Override
protected String emailField() {
return "email";
}

@Override
protected String idField() {
return "id";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package de.helfenkannjeder.come2help.server.security.oauth2;

import de.helfenkannjeder.come2help.server.configuration.security.ClientResourceDetails;
import de.helfenkannjeder.come2help.server.security.oauth2.token.AccessTokenService;
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CustomOAuthAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {

private final UserInfoTokenServices tokenService;
private final AccessTokenService accessTokenService;

public CustomOAuthAuthenticationProcessingFilter(String path, ClientResourceDetails clientResourceDetails, AccessTokenService accessTokenService) {
super(path);
this.tokenService = new UserInfoTokenServices(clientResourceDetails.getResource().getUserInfoUri(), clientResourceDetails.getClient().getClientId());
this.accessTokenService = accessTokenService;
}

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if (!"POST".equals(request.getMethod())) {
throw new BadCredentialsException("Only POST allowed.");
}

OAuth2AccessToken accessToken = accessTokenService.getAccessToken(request);

try {
return tokenService.loadAuthentication(accessToken.getValue());
} catch (InvalidTokenException e) {
throw new BadCredentialsException("Could not obtain user details from token", e);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package de.helfenkannjeder.come2help.server.security.oauth2;

import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest;
import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails;
import org.springframework.security.oauth2.common.OAuth2AccessToken;

import java.util.List;

/**
* @author Valentin Zickner
*/
public class OAuth2Client {

private static final String GRANT_TYPE = "password";

private final String tokenUrl;
private final String clientId;
private final String secret;

public OAuth2Client(String tokenUrl, String clientId, String secret) {
this.tokenUrl = tokenUrl;
this.clientId = clientId;
this.secret = secret;
}

public OAuth2AccessToken getAccessToken(String username, String password, List<String> scopes) {
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();

resource.setAccessTokenUri(tokenUrl);
resource.setClientId(clientId);
resource.setClientSecret(secret);
resource.setGrantType(GRANT_TYPE);
resource.setScope(scopes);

resource.setUsername(username);
resource.setPassword(password);

AccessTokenRequest atr = new DefaultAccessTokenRequest();
return new OAuth2RestTemplate(resource, new DefaultOAuth2ClientContext(atr)).getAccessToken();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package de.helfenkannjeder.come2help.server.security.oauth2.token;

import de.helfenkannjeder.come2help.server.configuration.security.ClientResourceDetails;
import org.springframework.security.oauth2.common.OAuth2AccessToken;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
* @author Valentin Zickner
*/
public interface AccessTokenService {
void setClientResourceDetails(ClientResourceDetails clientResourceDetails);
OAuth2AccessToken getAccessToken(HttpServletRequest httpServletRequest) throws IOException;
}
Original file line number Diff line number Diff line change
@@ -1,45 +1,40 @@
package de.helfenkannjeder.come2help.server.configuration.security;
package de.helfenkannjeder.come2help.server.security.oauth2.token;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices;
import de.helfenkannjeder.come2help.server.configuration.security.ClientResourceDetails;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.client.resource.UserRedirectRequiredException;
import org.springframework.security.oauth2.client.token.AccessTokenRequest;
import org.springframework.security.oauth2.client.token.DefaultAccessTokenRequest;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;

public class CustomOAuthAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

private final ClientResourceDetails clientResourceDetails;
private final UserInfoTokenServices tokenService;
private final AuthorizationCodeAccessTokenProvider accessTokenProvider = new AuthorizationCodeAccessTokenProvider();
/**
* @author Valentin Zickner
*/
public class AuthorizationCodeAccessTokenService implements AccessTokenService {

private ClientResourceDetails clientResourceDetails;
private final MappingJackson2HttpMessageConverter jsonMessageConverter;
private final AuthorizationCodeAccessTokenProvider accessTokenProvider = new AuthorizationCodeAccessTokenProvider();

public CustomOAuthAuthenticationProcessingFilter(String path, ClientResourceDetails clientResourceDetails, MappingJackson2HttpMessageConverter jsonMessageConverter) {
super(path);
this.clientResourceDetails = clientResourceDetails;
this.tokenService = new UserInfoTokenServices(clientResourceDetails.getResource().getUserInfoUri(), clientResourceDetails.getClient().getClientId());
this.accessTokenProvider.setStateMandatory(false);
public AuthorizationCodeAccessTokenService(MappingJackson2HttpMessageConverter jsonMessageConverter) {
this.jsonMessageConverter = jsonMessageConverter;
this.accessTokenProvider.setStateMandatory(false);
}

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if (!"POST".equals(request.getMethod())) {
throw new BadCredentialsException("Only POST allowed.");
}
public void setClientResourceDetails(ClientResourceDetails clientResourceDetails) {
this.clientResourceDetails = clientResourceDetails;
}

@Override
public OAuth2AccessToken getAccessToken(HttpServletRequest request) throws IOException {
OAuth2AccessToken accessToken;
try {
AccessTokenRequest accessTokenRequest = createAccessTokenRequest(request);
Expand All @@ -48,11 +43,7 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ
throw new BadCredentialsException("Could not obtain access token", e);
}

try {
return tokenService.loadAuthentication(accessToken.getValue());
} catch (InvalidTokenException e) {
throw new BadCredentialsException("Could not obtain user details from token", e);
}
return accessToken;
}

private AccessTokenRequest createAccessTokenRequest(HttpServletRequest request) throws IOException {
Expand Down Expand Up @@ -81,4 +72,6 @@ public String getCode() {
return code;
}
}


}
Loading