The new Certification Class of Learn Spring Security is out:

>> CHECK OUT THE COURSE

1. Overview

In this tutorial, we’ll use Spring Security OAuth to authenticate with the Reddit API.

2. Maven Configuration

First, in order to use Spring Security OAuth – we need to add the following dependency to our pom.xml (of course along any other Spring dependency you might use):

<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>2.0.6.RELEASE</version>
</dependency>

3. Configure OAuth2 Client

Next – let’s configure our OAuth2 client – the OAuth2RestTemplate – and a reddit.properties file for all the authentication related properties:

@Configuration
@EnableOAuth2Client
@PropertySource("classpath:reddit.properties")
protected static class ResourceConfiguration {

    @Value("${accessTokenUri}")
    private String accessTokenUri;

    @Value("${userAuthorizationUri}")
    private String userAuthorizationUri;

    @Value("${clientID}")
    private String clientID;

    @Value("${clientSecret}")
    private String clientSecret;

    @Bean
    public OAuth2ProtectedResourceDetails reddit() {
        AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
        details.setId("reddit");
        details.setClientId(clientID);
        details.setClientSecret(clientSecret);
        details.setAccessTokenUri(accessTokenUri);
        details.setUserAuthorizationUri(userAuthorizationUri);
        details.setTokenName("oauth_token");
        details.setScope(Arrays.asList("identity"));
        details.setPreEstablishedRedirectUri("http://localhost/login");
        details.setUseCurrentUri(false);
        return details;
    }

    @Bean
    public OAuth2RestTemplate redditRestTemplate(OAuth2ClientContext clientContext) {
        OAuth2RestTemplate template = new OAuth2RestTemplate(reddit(), clientContext);
        AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain(
          Arrays.<AccessTokenProvider> asList(
            new MyAuthorizationCodeAccessTokenProvider(), 
            new ImplicitAccessTokenProvider(), 
            new ResourceOwnerPasswordAccessTokenProvider(),
            new ClientCredentialsAccessTokenProvider())
        );
        template.setAccessTokenProvider(accessTokenProvider);
        return template;
    }

}

And “reddit.properties“:

clientID=xxxxxxxx
clientSecret=xxxxxxxx
accessTokenUri=https://www.reddit.com/api/v1/access_token
userAuthorizationUri=https://www.reddit.com/api/v1/authorize

You can get your own secret code by creating a Reddit app from https://www.reddit.com/prefs/apps/

We’re going to use the OAuth2RestTemplate to:

  1. Acquire the access token needed to access the remote resource.
  2. Access the remote resource after getting the access token.

Also note how we added the scope “identity” to Reddit OAuth2ProtectedResourceDetails so that we can retrieve the users account information later.

4. Custom AuthorizationCodeAccessTokenProvider

The Reddit OAuth2 implementation is a little different from the standard. And so – instead of elegantly extending the AuthorizationCodeAccessTokenProvider – we need to actually override some portions of it.

There are github issues tracking improvements that will make this not necessary, but these issues are not yet done.

One of the non-standard things that Reddit does is – when we redirect the user and prompt him to authenticate with Reddit, we need to have some custom parameters in the redirect URL. More specifically – if we’re asking for a permanent access token from Reddit – we need to add a parameter “duration” with the value “permanent“.

So, after extending AuthorizationCodeAccessTokenProvider – we have added this parameter in the getRedirectForAuthorization() method:

    requestParameters.put("duration", "permanent");

You can check the full source code from here.

5. The ServerInitializer

Next – let’s create our custom ServerInitializer.

We need to add a filter bean with id oauth2ClientContextFilter, so that we can use it to store the current context:

public class ServletInitializer extends AbstractDispatcherServletInitializer {

    @Override
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext context = 
          new AnnotationConfigWebApplicationContext();
        context.register(WebConfig.class, SecurityConfig.class);
        return context;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

    @Override
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        super.onStartup(servletContext);
        registerProxyFilter(servletContext, "oauth2ClientContextFilter");
        registerProxyFilter(servletContext, "springSecurityFilterChain");
    }

    private void registerProxyFilter(ServletContext servletContext, String name) {
        DelegatingFilterProxy filter = new DelegatingFilterProxy(name);
        filter.setContextAttribute(
          "org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher");
        servletContext.addFilter(name, filter).addMappingForUrlPatterns(null, false, "/*");
    }
}

6. MVC Configuration

Now – let’s take a look at our MVC configuration of our simple web-app:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "org.baeldung.web" })
public class WebConfig extends WebMvcConfigurerAdapter {

    @Bean
    public static PropertySourcesPlaceholderConfigurer 
      propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/jsp/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

    @Override
    public void configureDefaultServletHandling(
      DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        super.addViewControllers(registry);
        registry.addViewController("/home.html");
    }
}

7. Security Configuration

Next – let’s take a look at the main Spring Security configuration:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) 
      throws Exception {
        auth.inMemoryAuthentication();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .anonymous().disable()
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/home.html").hasRole("USER")
            .and()
            .httpBasic()
            .authenticationEntryPoint(oauth2AuthenticationEntryPoint());
    }

    private LoginUrlAuthenticationEntryPoint oauth2AuthenticationEntryPoint() {
        return new LoginUrlAuthenticationEntryPoint("/login");
    }
}

Note: We added a simple security configuration that redirect to “/login” which get the user information and load authentication from it – as explained in the following section.

8. RedditController

Now – let’s take a look at our controller RedditController.

We use method redditLogin() to get the user information from his Reddit account and load an authentication from it – as in the following example:

@Controller
public class RedditController {

    @Autowired
    private OAuth2RestTemplate redditRestTemplate;

    @RequestMapping("/login")
    public String redditLogin() {
        JsonNode node = redditRestTemplate.getForObject(
          "https://oauth.reddit.com/api/v1/me", JsonNode.class);
        UsernamePasswordAuthenticationToken auth = 
          new UsernamePasswordAuthenticationToken(node.get("name").asText(), 
          redditRestTemplate.getAccessToken().getValue(), 
          Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
        
        SecurityContextHolder.getContext().setAuthentication(auth);
        return "redirect:home.html";
    }

}

An interesting detail of this deceptively simple method – the reddit template checks if the access token is available before executing any request; it acquires a token if one is not available.

Next – we present the information to our very simplistic front end.

9. home.jsp

Finally – let’s take a look at home.jsp – to display the information retrieved form user’s Reddit account:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<html>
<body>
    <h1>Welcome, <small><sec:authentication property="principal.username" /></small></h1>
</body>
</html>

10. Conclusion

In this introductory article, we explored authenticating with the Reddit OAuth2 API and displaying some very basic information in a simple front end.

Now that we’re authenticated, we’re going to explore doing more interesting things with the Reddit API in the next article of this new series.

The full implementation of this tutorial can be found in the github project – this is an Eclipse based project, so it should be easy to import and run as it is.

Go deeper into Spring Security with the course:

>> LEARN SPRING SECURITY

  • TEST

    Could you please provide the complete source code? It will be great help for all your followers, if you’d allow us to do that.

    • Not sure what you mean – there’s a link at the end of the article to the full source code over on github 🙂

      • cristian borta

        The link provided is broken, Eugen. Could you please fix this issue? Thanks

  • hopeless

    I have this exception “org.springframework.security.authentication.InsufficientAuthenticationException: Authentication is required to obtain an access token (anonymous not allowed)” when i try to login. Can you tell me how to fix this exception?

    • Hey,
      So – not sure how you ran into that – is there maybe a quick test that we can use to replicate it? Or some scenario? If it simply happens on login – what’s the full stack there?

      • hopeless

        org.springframework.security.authentication.InsufficientAuthenticationException: Authentication is required to obtain an access token (anonymous not allowed)
        at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:88)
        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)
        at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:565)
        at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128)
        at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:530)
        at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:237)
        at com.hopeless.web.controller.LoginController.login(LoginController.java:47)

        Is it have to authentication in localhost before authentication in reddit?

        • I’ve tried to replicate the issue without luck. If you can get a reproducible sequence of steps that’s going to help flush this out, I’d be happy to try again.
          Also – let’s move this to an issue over on Github – it’s going to be easier than having a technical discussion here. Cheers,
          Eugen.

  • MoVa

    Hi Eugen, very nice tutorial. I have a questions, I want to create mobile app (in this time android) and spring boot rest server site. For authentication in first time I used classic user password spring security way but with rest not MVC (post username and password in to the default spring security login page url from my mobile app) it worked but its is not a ideal because a had a problem with session time out on mobile device. My second way I used spring oauth grand type user and password it works very good i have a refresh access token. Now i want to add login to my mobile app with facebook. What is the beast way ? A read lot of pages but in all time use spring social with mvc not with rest and mobile application, and all the time focused only on login but not other communication with server and mobile application after login with facebook.

    My first scenario is –
    1. login to facebook by mobile app – facebook return access token.
    2. Send access token – to server (https post call)
    3. On server validate token with spring facebook social, find my application user by facebook user id.
    4. Authenticate this user to may app.

    I works but problem with session time out because facebook token is valid (user in application is login but session expiration when user use app next time)

    Is it possible use facebook access token like authentication code and after create custom access token for my mobile app? or create custom grand type for facebook token ? What is the best way to send facebook access token from my mobile client and authenticate this user to my server and then start communication with this mobile app ?

    Thanks, do you have same information on this topic in your Spring Video Courses?

    • Hey MoVa,
      That’s a relatively standard scenario you have, where you just want to use an Android app as a client. Two quick things here. First – I haven’t worked with the Facebook API, so I can’t be to specific, but if you have a refresh token, you’ll of course be able to use that to refresh your access token when it expires – hence you’ll be able to work around your session expiration problems. Now – on to the communication side of things, once you have your access token, you’ll simply be able to hit the API and consume your resources by sending that in the Authorization header of your HTTP requests. There’s nothing really special about that part – so there’s not much to document, once you have your valid token.
      And the second quick thing is that – while the course does deal with OAuth2 quite a bit, it’s not focused on mobile apps, so I wouldn’t recommend getting the course for that aspect alone.
      Hope that helps. Cheers,
      Eugen.

  • Joseph Stoll

    This article is amazing. I’ve had a lot of trouble finding something this clear while working on my current Spring Security problem.

    Question: In the bean redditRestTemplate(OAuth2ClientContext clientContext), how does Spring know where to get the Oauth2ClientContext from? I checked your full source code in Github, and I don’t see it configured anywhere. It feels as though it appears by magic.

    I’m having some troubles initializing this bean and wanted to see if there was anything I was missing.

    • Glad you like the article Joseph – it’s the start of a pretty long series that all started with this one. So, like many other beans, the OAuth client context is defined by the framework itself. Not sure if it’s coming out of Boot, but you should be able to easily trace back the creation of the bean by simply debugging through the context bootstrapping process and seeing where it gets created.
      Hope that helps. Cheers,
      Eugen.

  • Alex

    How such a small project has turned into a huge?

    • Hey Alex – that’s a good question.
      The answer is – it started as a POC and evolved into a system I am actually using, which means it has to be production-grade, not just an example. That’s why the project grow.
      Of course, ultimately – it’s still a very small project with only a couple of features.
      Hope that answers your question. Cheers,
      Eugen.

  • Quan Ding

    Hi Eugen, Great tutorial! The github code is quite different from the article now. Can you explain how to run the sample code in github to see it in action?
    I’m trying to create a angular single page app with Spring Boot that uses both the username/password login as well as social login. And my app itself is an AuthServer as well. I was having Angular app using Bearer tokens to authorize API requests to my resource server. But with the 3rd party social login, I don’t have Bearer tokens that I can use (or I don’t know how to generate my own access token based on tokens returned from 3rd party login) I have heard people saying I should not use bearer token between angular app and my server and should use session cookies instead. But I couldn’t get the cookies to work either.
    I tried to use your code as guides but got lost in the lines of code and don’t know where to look for answers.
    I have my sample app here on GitHub: https://github.com/dingquan/spring-angular-oauth
    Could you kindly point a direction where I can follow?

    • Hey Quan,
      First, I’m glad you’re enjoying the article. Yes, the codebase has evolved a bit, but it should still be very close to what the article shows here.
      Of course the article (and the codebase) cover a much simpler example than what you’re describing.
      Using session cookies to drive authentication takes away many of the advantages of using OAuth in the first place. Using cookies for storage only – that’s another thing.
      Now – when it comes to your repo – I generally try to help and have a look at code and questions from readers, but unfortunately I had to cut back on exploring full repos like this lately – just because of sheer volume of emails/comments.
      My suggestion is to break your problem down into small, clear, well-defined pieces. That way you can get help easier (here, on StackOverflow, over email, etc).
      Hope that helps. Cheers,
      Eugen.

  • Mp

    Hi Eugen, I tried to replicate the code in your tutorial but it wasn’t enough for running it so at the end I ended up merging codes of different sites XD… this is my code working without to use spring-boot,

    https://github.com/mplescano/springOauthWithoutBoot

    • Grzegorz Piwowarek

      Mp, what did you exactly have problems with? Let us know. We will update the article if needed

  • Bhaumik Thakkar

    Hi how can I disable userAuthorization?