1. Introduction

In this tutorial, we will look at how we can use Spring Security‘s OAuth 2.0 support to authenticate with Amazon Cognito.

Along the way, we’ll briefly take a look at what Amazon Cognito is and what kind of OAuth 2.0 flows it supports.

In the end, we’ll have a simple one-page application. Nothing fancy.

2. What Is Amazon Cognito?

Cognito is a user identity and data synchronization service that makes it easy for us to manage user data for our apps across multiple devices.

With Amazon Cognito, we can:

  • create, authenticate, and authorize users for our applications
  • create identities for users of our apps who use other public identity providers like Google, Facebook, or Twitter
  • save our app’s user data in key-value pairs

3. Setup

3.1. Amazon Cognito Setup

As an Identity Provider, Cognito supports the authorization_code, implicit, and client_credentials grants. For our purposes, let’s set things up to use the authorization_code grant type.

First, we need a bit of Cognito setup:

In the configuration of the application client, make sure the CallbackURL matches the redirect-uri from the Spring config file. In our case, this will be:

http://localhost:8080/login/oauth2/code/cognito

The Allowed OAuth flow should be Authorization code grant. Then, on the same page*,* we need to set the Allowed OAuth scope to openid.

To redirect the user to Cognito’s custom login page, we also need to add a User Pool Domain.

3.2. Spring Setup

Since we want to use OAuth 2.0 Login, we’ll need to add the spring-security-oauth2-client and spring-security-oauth2-jose dependencies to our application:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-jose</artifactId>
</dependency>

And then, we’ll need some configuration to bind everything together:

spring:
  security:
    oauth2:
      client:
        registration:
          cognito:
            clientId: clientId
            clientSecret: clientSecret
            scope: openid
            redirect-uri: http://localhost:8080/login/oauth2/code/cognito
            clientName: clientName
        provider:
          cognito:
            issuerUri: https://cognito-idp.{region}.amazonaws.com/{poolId}
            user-name-attribute: cognito:username

In the above configuration, the properties clientId, clientSecret, clientName and issuerUri should be populated as per our User Pool and App Client created on AWS.

And with that, we should have Spring and Amazon Cognito set up! The rest of the tutorial defines our app’s security configuration and then just ties up a couple of loose ends.

3.3. Spring Security Configuration

Now we’ll add a security configuration class:

@Configuration
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf()
            .and()
            .authorizeRequests(authz -> authz.mvcMatchers("/")
                .permitAll()
                .anyRequest()
                .authenticated())
            .oauth2Login()
            .and()
            .logout()
            .logoutSuccessUrl("/");
        return http.build();
    }
}

Here we first specified that we need protection against CSRF attacks and then permitted everyone access to our landing page. After that, we added a call to oauth2Login to wire in the Cognito client registration.

4. Add a Landing Page

Next, we add a simple Thymeleaf landing page so that we know when we’re logged in:

<div>
    <h1 class="title">OAuth 2.0 Spring Security Cognito Demo</h1>
    <div sec:authorize="isAuthenticated()">
        <div class="box">
            Hello, <strong th:text="${#authentication.name}"></strong>!
        </div>
    </div>
    <div sec:authorize="isAnonymous()">
        <div class="box">
            <a class="button login is-primary" th:href="@{/oauth2/authorization/cognito}">
              Log in with Amazon Cognito</a>
        </div>
    </div>
</div>

Simply put, this will display our user name when we’re logged in or a login link when we’re not. Pay close attention to what the link looks like since it picks up the cognito part from our configuration file.

And then let’s make sure we tie the application root to our welcome page:

@Configuration
public class CognitoWebConfiguration implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("home");
    }
}

5. Run the App

This is the class that will put everything related to auth in motion:

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

Now we can start our application, go to http://localhost:8080, and click the login link. On entering credentials for the user we created on AWS, we should be able to see a Hello, username message.

6. Conclusion

In this tutorial, we looked at how we can integrate Spring Security with Amazon Cognito with just some simple configuration. And then we put everything together with just a few pieces of code.

As always, the code presented in this article is available over on Github.