0

I am trying to access HttpServletRequest or HttpSession in my service component. The service component is where github OAuth2 login is being processed. Below is my service code.

@RequiredArgsConstructor
@Service
public class GithubOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
    private final UserRepository userRepository;
    private final JwtTokenUtil jwtTokenUtil;
    private final HttpServletRequest request;

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultOAuth2UserService();
        OAuth2User oAuth2User = delegate.loadUser(userRequest);

        String userNameAttributeName = userRequest.getClientRegistration()
                .getProviderDetails().getUserInfoEndpoint()
                .getUserNameAttributeName();

        OAuthAttributes attributes = OAuthAttributes.ofGithub(userNameAttributeName, oAuth2User.getAttributes());

        User user = saveOrFindUser(attributes);
       
        request.setAttribute("token", jwtTokenUtil.generateAccessToken(user.getId(), user.getRole()));

        return new DefaultOAuth2User(
                Collections.singleton(new SimpleGrantedAuthority(user.getRole().name())),
                attributes.getAttributes(),
                attributes.getNameAttributeKey()
        );
    }


    private User saveOrFindUser(OAuthAttributes attributes) {
        Optional<User> optionalUser = userRepository.findByEmail(attributes.getEmail());
        if(optionalUser.isPresent()) {
            return optionalUser.get();
        } else {
            return userRepository.save(attributes.toEntity());
        }
    }
}

And below is my Spring Security configuration class.

@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final GithubOAuth2UserService githubOAuth2UserService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .httpBasic().disable()
                .headers().frameOptions().disable()
                .and().csrf().disable()
                .cors().configurationSource(corsConfigurationSource())
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                    .authorizeRequests()
                        .antMatchers("/v1/health-check")
                        .permitAll()
                .and()
                    .logout()
                        .logoutSuccessUrl("/")
                .and()
                    .oauth2Login()
                        .successHandler(authenticationSuccessHandler())
                        .failureHandler(authenticationFailureHandler())
                        .userInfoEndpoint()
                        .userService(githubOAuth2UserService);
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.addAllowedOriginPattern("*");
        configuration.addAllowedHeader("*");
        configuration.addAllowedMethod("*");
        configuration.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler() {
        return new GithubOAuthExceptionHandler();
    }

    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler() {
        return new GithubOAuthOnSuccessHandler();
    }
}

I have tried to autowire HttpSession and HttpServletRequest using Lombok's @RequiredArgsConstructor, and also tried the way below.

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

And I am getting the error below.

java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.

I am trying to access HttpServletRequest or HttpSession in a @Service component, but I cannot understand why this error is occuring. Are there any extra configurations to access these classes in components? I am using spring boot 2.4.3.

Roy Ra
  • 504
  • 1
  • 6
  • 23
  • Try this [answer](https://stackoverflow.com/a/1795931/5342600) discussed in similar question. – Chintan Radia Mar 15 '21 at 04:41
  • Yes, but as I mentioned above, both answers on the similar questions didn't work for me. – Roy Ra Mar 15 '21 at 05:46
  • 1
    If the answers didn't work for you, I wonder if the `RequestContextFilter` is in your filter chain. The filter populates the `RequestContextHolder` on each request. – jzheaux Mar 15 '21 at 21:43

2 Answers2

1

I resolved this issue by using comment's expectation.

The answer was to register RequestContextListener as a spring bean in spring configuration class.

@SpringBootApplication
@EnableJpaAuditing
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public RequestContextListener requestContextListener() {
        return new RequestContextListener();
    }
}
Roy Ra
  • 504
  • 1
  • 6
  • 23
0

I realize that this isn't the question that you asked, but if you are able, I'd recommend moving this work to the request layer instead.

Likely, there's value in your User object being in the SecurityContextHolder so that it can be accessed throughout your application.

So, first, if you create a class like so:

static class MyOAuth2User extends User implements OAuth2User {
    public MyOAuth2User(User user, OAuthAttributes attributes) {
        super(user);
    }

    public Map<String, Object> getAttributes() {
        return this.attributes.getAttributes();
    }

    public String getName() {
        return getAttribute(this.attributes.getNameAttributeKey());
    }

    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Collections.singleton(new SimpleGrantedAuthority(getRole().name()));
    }
}

Then that gives you the benefit of your User being a member of the security principal. Additionally, it benefits you because you can access it in your GitHubOAuthOnSuccessHandler, where you already have the HttpServletRequest object:

public void onAuthenticationSuccess(...) {
    User user = (User) authentication.getPrincipal();
    String token = jwtTokenUtil.generateAccessToken(user.getId(), user.getRole());
    request.setAttribute("token", token);
    // ...
}
jzheaux
  • 7,042
  • 3
  • 22
  • 36