SESSIONID injection for basic authentication requests - spring-security

I have a Spring-cloud based micro-services application. I also have an API gateway in front of all these services.
I need to support two types of clients.
One of them can call my application using an authorization token (by calling /authorize for example). The token is basically the SESSION ID. All the servers share the session by using Spring Session Redis.
The second client can only send me http basic authentication (user:pass as authorization header).
In the case of the second client, I need to check if the user is already authenticated and has an active session in redis. I added filter before BasicAuthenticationFilter in my security configuration to check that.
If the user has an active session, I'm putting the SESSIONID in the header, and removing the authorization header from the request (I'm using a custom HttpServletRequest wrapper for that). My purpose was that from that point on, Spring will manage the request in the downstream micro-services as if it was sent with a SESSIONID. The reason for that is to avoid a very long login time (more than 1 second).
Here’s my issue: when spring checks if the SESSIONID exists, it checks the original request which doesnt have any sessionId.
Security configuration:
#Resource
#Qualifier("sessions")
private Map<String, String> sessions;
#Autowired
#Qualifier("httpSessionStrategy")
HttpSessionStrategy sessionStrategy;
#Override
protected void configure(HttpSecurity http) throws Exception {
// // #formatter:off
http
.addFilterBefore(setSessionIdInHeader(), BasicAuthenticationFilter.class)
.sessionManagement()
.and()
.exceptionHandling()
.authenticationEntryPoint(restEntryPoint())
.and()
.headers().addHeaderWriter(new StaticHeadersWriter("Server",""))
.and()
.httpBasic()
.authenticationEntryPoint(restEntryPoint())
.and()
.logout().addLogoutHandler(clearTicketOnLogoutHandler())
.logoutSuccessHandler(logoutSuccessHandler())
.and()
.authorizeRequests()
.antMatchers("/index.html", "/login", "/").permitAll()
.antMatchers(HttpMethod.OPTIONS).denyAll()
.antMatchers(HttpMethod.HEAD).denyAll()
.anyRequest().authenticated()
.and()
.authenticationProvider(authenticationProvider)
.csrf()
.disable()
.addFilterAfter(ticketValidationFilter(), SessionManagementFilter.class)
.addFilterAfter(changePasswordFilter(), SessionManagementFilter.class)
.addFilterAfter(httpPolutionFilter(), SessionManagementFilter.class)
.addFilterAfter(saveSessionId(), SessionManagementFilter.class);
// #formatter:on
}
Filter to add header to request:
private Filter setSessionIdInHeader(){
return new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
Jedis jedis = null;
String authorization = request.getHeader("authorization");
String sessionId = null;
if (authorization != null){
if (sessions.get(authorization) != null){ //user already authenticated
sessionId = sessions.get(authorization);
jedis = getJedisPool().getResource();
if (jedis.hgetAll("spring:session:sessions:"+sessionId) != null){ //session alive in redis
log.info("session :"+ sessionId +" exists in redis");
HeaderMapRequestWrapper wrapper = new HeaderMapRequestWrapper(request);
wrapper.addHeader("TOKEN", sessionId);
wrapper.addHeader("mock_authorization", authorization);
filterChain.doFilter(wrapper, response);
}
}
}
filterChain.doFilter(request, response);
}
};
}
Change header name of SESSIONID:
#Bean
public HeaderHttpSessionStrategy httpSessionStrategy(){
HeaderHttpSessionStrategy headerHttpSessionStrategy = new HeaderHttpSessionStrategy();
headerHttpSessionStrategy.setHeaderName("TOKEN");
return headerHttpSessionStrategy;
}
private Filter saveSessionId() {
return new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if(request.getHeader("authorization") != null){
sessions.put(request.getHeader("authorization"), request.getSession().getId());
}else{
sessions.put(request.getHeader("mock_authorization"), request.getSession().getId());
}
}
};
}

Related

Spring OAuth2.0 : Spring Authorization Server 1.0 and Resource server in the same boot application

I'm struggling to use the very same Spring Boot 3.0 application as both authentication server and resource server, but until now, I've not been able to make the whole thing working.
First, I defined a very simple RestController:
#RestController
#RequestMapping("api")
public class PublicAPI {
#GetMapping("/apitest")
public String test(Principal principal) {
return " This is a test ==>";
}
}
Then, essentially following the code found in a Sample project of Spring, I managed to setup my boot app as Spring Authorization Server. I'm able to use Postman to get the authentication token using Oauth2 flow: I'm redirected to Spring's standard login page, I log in with credentials, and I get the Token.
Problem is, if I try to GET http://localhost:9000/api/apitest` using provided token, I get a 401 response from Spring boot.
This is my Security Configuration:
#Bean
#Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http, CorsConfiguration configCors) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults());
http
.exceptionHandling((exceptions) -> exceptions
.authenticationEntryPoint(
new LoginUrlAuthenticationEntryPoint("/login"))
);
http.cors().configurationSource(request -> configCors);
return http.build();
}
#Bean
#Order(2)
SecurityFilterChain apiFilter(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/**")
.authorizeHttpRequests()
.requestMatchers("/api/**").authenticated()
.and()
.oauth2ResourceServer()
.jwt();
return http.build();
}
#Bean
#Order(3)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http, CorsConfiguration configCors) throws Exception {
http
.securityMatcher("/oauth2/**", "/login")
.authorizeHttpRequests()
.requestMatchers("/login", "/oauth2/**")
.authenticated()
.and()
.formLogin(Customizer.withDefaults());
http.cors().configurationSource(request -> configCors);
return http.build();
}
#Bean
public CorsConfiguration corsConfiguration() throws Exception {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.setAllowedOriginPatterns(List.of("*"));
configuration.setAllowedMethods(List.of("*"));
configuration.setAllowedHeaders(List.of("*"));
return configuration;
}
If I try to access another Spring API in a different Spring Boot application which uses the first one as Authentication Server I get no errors.
Pretty sure that there's something wrong my configuration... any hint will be greatly appreciated !
At the very end, it turned out that another filter has been configured:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class LoopbackIpRedirectFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (request.getServerName().equals("localhost") && request.getHeader("host") != null) {
UriComponents uri = UriComponentsBuilder.fromHttpRequest(new ServletServerHttpRequest(request))
.host("127.0.0.1").build();
response.sendRedirect(uri.toUriString());
return;
}
filterChain.doFilter(request, response);
}
}
Removing the LoopbackIpRedirectFilter problem was fixed

Spring Security not sending samesite=none with JSESSIONID

This is how my websecurityconfig is described:
public void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(corsFilter(), SessionManagementFilter.class) //adds your custom CorsFilter
.authorizeRequests()
.antMatchers("/ping/get").permitAll()
.antMatchers("/user/updatePassword").permitAll()
.antMatchers("/user/resetPassword").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.successHandler(successHandler())
.failureHandler(failureHandler())
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
.authenticationEntryPoint(authenticationEntryPoint())
.and()
.logout()
.logoutSuccessUrl("/login")
.invalidateHttpSession(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessHandler((httpServletRequest, httpServletResponse, authentication) -> {
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
})
.permitAll()
;
http.csrf().disable();
http.headers()
.addHeaderWriter(
new StaticHeadersWriter("Access-Control-Allow-Origin", "http://localhost:4200")
);
http.headers()
.addHeaderWriter(
new StaticHeadersWriter("Access-Control-Allow-Credentials", "true")
);
}
and the cors filter i've defined the header for samesite=none as follows
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) servletResponse;
HttpServletRequest req= (HttpServletRequest) servletRequest;
res.setHeader("Access-Control-Allow-Origin", req.getHeader("origin"));
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
res.setHeader("Access-Control-Max-Age", "3600");
res.setHeader("Access-Control-Allow-Headers", "access-control-allow-origin,content-type");
res.addHeader("Access-Control-Expose-Headers", "xsrf-token");
res.setHeader("Set-Cookie", "HttpOnly;Secure;SameSite=None");
if ("OPTIONS".equals(req.getMethod())) {
res.setStatus(HttpServletResponse.SC_OK);
} else {
filterChain.doFilter(req, res);
}
}
#Override
public void destroy() {
}
}
But whenever i'm calling the login endpoint, i'm only receiving httponly,secure but nnot samesite=none with my JSESSIONID cookkie. How do i make this work?
i've tried all different filter from answers of other questons but none of them work.
The issue is only on chrome. It would be helpful if there is any workaroud also. Recently chrome removed the samesite flag which could be disabled. need to solve this in order to create a webview for my site.
You can set the SameSite attribute when using Spring Session with a custom CookieSerializer.
#Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("JSESSIONID");
serializer.setSameSite("None");
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
return serializer;
}

Spring Security protect routes hasRole, hasAuthority do not work with CORS http request

I'm using Spring Security JWT to secure Angular login page.
I configure Spring CORS filter to accept http CORS request.
I verify the JWT is valid in return and authenticated with correct roles.
The secure routes such as hasRole, hasAuthority fail on CORS requests, it always return 403 status.
//http config
protected void configure(HttpSecurity http) throws Exception{
http.cors().and()
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/list_customers").hasAnyRole("ADMIN","DEV") //FAIL 403 error
.antMatchers(HttpMethod.GET, "/api/list_customers").hasAuthority("ROLE_ADMIN") //FAIL 403 error
.anyRequest().authenticated()
}
//CORS filter
public class CORSFilter extends GenericFilterBean implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
HttpServletResponse httpResp = (HttpServletResponse) response;
httpResp.setHeader("Access-Control-Allow-Origin", "*");
httpResp.setHeader("Access-Control-Allow-Methods", "*");
httpResp.setHeader("Access-Control-Allow-Headers", "*");
httpResp.setHeader("Access-Control-Allow-Credentials", "true");
httpResp.setHeader("Access-Control-Max-Age", "3600");
chain.doFilter(request, response);
}
}
Delete your filter and replace it with
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOrigin("*");
configuration.addAllowedMethod("*");
configuration.addAllowedHeader("*");
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600l);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
Reference
Have a loot at org.springframework.web.filter.CorsFilter to see how it picks up CorsConfigurationSource bean and configures the cors filter
As you can see in the chain of filters, CorsFilter is earlier in the chain than filters checking if the user is authenticated or if they have right permission. Your 403 is thrown much earlier by the CorsFilter as it is not configured correctly
Issue Resolved.
I found out my production role name is different.
I changed the role name from ADMIN to ROLE_ADMIN, then it works fine.

How to pass parameter along with logout success url in spring security?

I am using java based spring security configuration in my spring boot application. When user clicks on logout link, user is redirected to the login page. Here, in this case, I need to pass a custom parameter in the logout success url.
e.g. when I logout, app is redirected to http://localhost:8080/app/login
But I want it to have a parameter like below
http://localhost:8080/app/login?idletimeout=true
I have created my custom LogoutSuccesshandle for this. I get the param value in the handler and then I construct the success url and then redirect to it. But then on logout that parameter goes missing.
Below is my handler code.
public class LogoutSuccessHandlerImpl extends SimpleUrlLogoutSuccessHandler {
private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
request.getSession().invalidate();
SecurityContextHolder.clearContext();
request.setAttribute("isLoggedOut", "true");
String contextPath = request.getContextPath();
String redirectURL = "/login";
String isIdleTimeOut = request.getParameter("idleTimeout");
request.setAttribute("idleTimeout", isIdleTimeOut);
System.out.println(isIdleTimeOut + " isIdleTimeOut ");
if (isIdleTimeOut != null && isIdleTimeOut.equalsIgnoreCase("true")) {
System.out.println("in if ");
redirectURL += "?idleTimeout=" + isIdleTimeOut;
}
// setDefaultTargetUrl(redirectURL);
// response.sendRedirect(redirectURL);
// super.onLogoutSuccess(request, response, authentication);
redirectStrategy.sendRedirect(request, response, redirectURL);
}
Below is my java config code.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/checkLogin")
.defaultSuccessUrl("/home")
.failureUrl("/login?login_error=1")
.usernameParameter("username")
.passwordParameter("password")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(new LogoutSuccessHandlerImpl())
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/error**").permitAll()
.antMatchers("/checkLogin**").permitAll()
.anyRequest()
.authenticated()
.accessDecisionManager(accessDecisionManager)
.and()
.exceptionHandling()
.accessDeniedPage("/accessDenied")
.and()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.invalidSessionUrl("/login")
.maximumSessions(1);
}
What you can do is to prepare your own logout method (a custom logout url) for your applicatoin:
1) Prepare your LogoutController:
#Controller
#RequestMapping(value = "/logout")
public class LogoutController {
#RequestMapping(value = {"", "/"})
public String logout(HttpServletRequest request) {
SecurityContextHolder.clearContext();
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
return "redirect:" + <your-logout-success-url>;
}
}
Here, invalidating the session and clearing the context provides you a logout mechanism.
2) Update your logout urls inside jsp files:
<span>Log out</span>
3) In addition, for default "log out" scenarios, you can still continue to use the Spring Security log out:
<logout logout-success-url="/" invalidate-session="true" delete-cookies="JSESSIONID"
logout-url="/j_spring_security_logout"/>
So, in this logout method, you can do whatever you want with the request object when user demands the logout process. You do not need to pass parameters now.
It should be something like this
http
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/login?idletimeout=true"));
In xml
<logout invalidate-session="true" logout-success-url="/login?idletimeout=true"/>

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:-
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?
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://kumarsambhavjain#bitbucket.org/kumarsambhavjain/spring-oauth2-login.git
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
I used your code to create a OAuth2 Spring Boot sample, quite similar, and I had same issue once I replaced profile URL to:
https://www.googleapis.com/plus/v1/people/me/openIdConnect
I resolved it by enabling Google+ API in Google Console:
Visit the Google API Console here:https://code.google.com/apis/console/?api=plus
Under the Services panel, make sure the Google+ API is turned "on".
In the APIs console, click API Access in the left menu.
Copy the API key presented towards the bottom. Include this API key in your HTTP request.
This process is explained in this question:
How to call https://www.googleapis.com/plus/v1/people/me at google

Resources