Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security OAuth2 Java Config for Google Login

I am migrating working XML configuration to Java configuration for Spring Security OAuth2 and using Google as the OAuth provider.

This is how my java configuration looks:

@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private static final List<String> scope;

    static {
        // Permissions to access email and profile
        scope = new ArrayList<>(3);
        scope.add("openid");
        scope.add("email");
        scope.add("profile");
    }

    @Autowired(required = true)
    private UserService userService;

    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http.
            authorizeRequests()
                .antMatchers(HttpMethod.GET, "/","/public/**", "/resources/**","/resources/public/**").permitAll()
                //.antMatchers("/google_oauth2_login").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                    .loginPage("/")
                    .loginProcessingUrl("/login")
                    .defaultSuccessUrl("/home")
                    .and()
                    .csrf().disable()
                    .logout()
                        .logoutSuccessUrl("/")
                        .logoutUrl("/logout")
                .and()
                    .requiresChannel().anyRequest().requiresSecure()
                .and()
                    .addFilterAfter(oAuth2ClientContextFilter(),ExceptionTranslationFilter.class)
                    .addFilterAfter(googleOAuth2Filter(),OAuth2ClientContextFilter.class)
                .userDetailsService(userService);
        // @formatter:on
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
            throws Exception {
        // @formatter:off
        auth
            .authenticationProvider(googleOauth2AuthProvider())
            .userDetailsService(userService);
        // @formatter:on
    }

    @Bean
    public GoogleOAuth2Filter googleOAuth2Filter() throws Exception {
        GoogleOAuth2Filter filter = new GoogleOAuth2Filter(
                "/google_oauth2_login",
                "https://accounts.google.com/o/oauth2/auth",
                oAuth2RestTemplate(auth2ProtectedResourceDetails()));
        filter.setAuthenticationManager(authenticationManagerBean());
        return filter;
    }

    @Bean
    public GoogleOauth2AuthProvider googleOauth2AuthProvider() {
        GoogleOauth2AuthProvider authProvider = new GoogleOauth2AuthProvider();
        return authProvider;
    }

    @Bean
    public OAuth2ProtectedResourceDetails auth2ProtectedResourceDetails() {
        AuthorizationCodeResourceDetails auth2ProtectedResourceDetails = new AuthorizationCodeResourceDetails();
        auth2ProtectedResourceDetails
                .setClientAuthenticationScheme(AuthenticationScheme.form);
        auth2ProtectedResourceDetails
                .setAuthenticationScheme(AuthenticationScheme.form);
        auth2ProtectedResourceDetails.setGrantType("authorization_code");
        auth2ProtectedResourceDetails
                .setClientId("the-client-id");
        auth2ProtectedResourceDetails
                .setClientSecret("the-client-secret");
        auth2ProtectedResourceDetails
                .setAccessTokenUri("https://accounts.google.com/o/oauth2/token");
        auth2ProtectedResourceDetails.setScope(scope);
        auth2ProtectedResourceDetails
                .setUserAuthorizationUri("https://accounts.google.com/o/oauth2/auth");
        auth2ProtectedResourceDetails.setUseCurrentUri(false);
        auth2ProtectedResourceDetails
                .setPreEstablishedRedirectUri("https://localhost/google_oauth2_login");
        return auth2ProtectedResourceDetails;
    }

    @Bean
    public OAuth2RestTemplate oAuth2RestTemplate(
            OAuth2ProtectedResourceDetails resource) {
        OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(resource);
        return oAuth2RestTemplate;
    }

    @Bean
    public OAuth2ClientContextFilter oAuth2ClientContextFilter() {
        OAuth2ClientContextFilter oAuth2ClientContextFilter = new OAuth2ClientContextFilter();
        return oAuth2ClientContextFilter;
    }

}

Note that I have disabled CSRF. From my login page the user do gets redirected to Google login page

Problem:-

  1. Google Permission Page just asks for "Have offline access".'Email/Profile' access request is missing.

The equivalent 'scope' attibute XML configuration :-

<oauth2:resource id="googleOauth2Resource" type="authorization_code"
        client-id="the-client-id
        client-secret="the-client-secret"
        user-authorization-uri="https://accounts.google.com/o/oauth2/auth"
        scope="openid email profile" use-current-uri="false"
        client-authentication-scheme="form" pre-established-redirect-uri="https://localhost/google_oauth2_login" />

do correctly asks for email and profile permissions. Why?

  1. Continuing anyway with the 'Have offline access' results in this exception:-
    org.springframework.web.client.HttpClientErrorException: 400 Bad Request
        at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
        at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport$AccessTokenErrorHandler.handleError(OAuth2AccessTokenSupport.java:243)
        at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:592)
        at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:550)
        at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:514)
        at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.obtainAuthorizationCode(AuthorizationCodeAccessTokenProvider.java:145)
        at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.obtainAccessToken(AuthorizationCodeAccessTokenProvider.java:196)
        at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:142)
        at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:118)
        at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221)
        at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173)
        at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105)

while trying to get user profile using this code block:

@Override
public Authentication attemptAuthentication(HttpServletRequest request,
        HttpServletResponse response) throws AuthenticationException,
        IOException, ServletException {
    logger.info("Google Oauth Filter Triggered!!");
    SecurityContext context = SecurityContextHolder.getContext();
    // auth null or not authenticated.
    String code = request.getParameter("code");
    Authentication dummyAuthentication = null;

    if (StringUtils.isEmpty(code)) {
        // Google authentication in progress. will return null.
        logger.debug("Will set dummy user in context ");
        dummyAuthentication = getDummyAuthenticationToken();
        context.setAuthentication(dummyAuthentication);
        // trigger google oauth2.
        oauth2RestTemplate.postForEntity(authURI, null, Object.class);
        return null;
    } else {
        // response from google received !!.
        // remove dummy authentication from context.
        //SecurityContextHolder.clearContext();
        logger.debug("Response from Google Recieved !!");
        // get user profile and prepare the authentication token object.
        ResponseEntity<Object> forEntity = oauth2RestTemplate.getForEntity(
                HTTPS_WWW_GOOGLEAPIS_COM_PLUS_V1_PEOPLE_ME_OPEN_ID_CONNECT,
                Object.class);

        @SuppressWarnings("unchecked")
        Map<String, String> profile = (Map<String, String>) forEntity
                .getBody();

        CustomOAuth2AuthenticationToken authenticationToken = getOAuth2Token(
                profile.get(EMAIL), profile.get(NAME));
        authenticationToken.setAuthenticated(false);
        return getAuthenticationManager().authenticate(authenticationToken);
    }
}

Spring RestTemplate showing this in logs:

o.s.web.client.RestTemplate              : POST request for "https://accounts.google.com/o/oauth2/auth" resulted in 400 (Bad Request); invoking error handler
2014-09-05 21:51:46.870  WARN 5836 --- [ qtp25546756-15] o.eclipse.jetty.servlet.ServletHandler   : /google_oauth2_login

This same piece of code works while using with XML configuration.

UPDATE 1

I was able to fix the 'Offline Access' problem by changing scope to 'https://www.googleapis.com/auth/plus.profile.emails.read' & 'https://www.googleapis.com/auth/plus.login'.

Still getting bad request error while trying to get user profile

Please find source code for the problem here - git clone https://[email protected]/kumarsambhavjain/spring-oauth2-login.git

like image 829
Kumar Sambhav Avatar asked Aug 04 '14 16:08

Kumar Sambhav


1 Answers

Have you tried change profile URL to

https://www.googleapis.com/plus/v1/people/me/openIdConnect

See more: https://developers.google.com/+/api/openidconnect/getOpenIdConnect

like image 108
Thanh Nguyen Van Avatar answered Nov 06 '22 13:11

Thanh Nguyen Van



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!