How to secure Spring boot REST API endpoints with Amazon Cognito
Tags: java, api, aws, cloud
Published: 2022-06-02 12:03:48

Hello! For Java backend developers, with [Spring security](https://docs.spring.io/spring-security/reference/getting-spring-security.html) there are many ways to secure [Spring boot](https://spring.io/projects/spring-boot) Rest APIs. 

Usually, implementing Spring boot bearer token functionality for Rest API requires many lines of code, hours, and the need for user management. However, using Spring boot AWS authentication thanks to [Amazon Cognito](https://aws.amazon.com/cognito/), things become very simple and very very fast. We no longer need to manage users, [Amazon Cognito user pool](https://aws.amazon.com/cognito/) takes care of that. Cognito also allows us to share users between many Rest APIs and front-ends (web, mobile).

## Create Amazon Cognito User Pool

1. Sign in into [Amazon console](https://console.aws.amazon.com/console/home), and then search for : Cognito.
![Amazon Cognito page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5mzr8zys2pwa7imymvtf.png)

2. Click on **Create User Pool**. 

3. In the _Configure sign-in experience_ step, check **User name**, **Email** and **Allow users to sign in with a preferred user name** options then click on **Next**.

4. In the _Configure security requirements_ step, leave all the default options except **MFA enforcement** where you should choose **No MFA** instead of **Require MFA - Recommended**, then click on **Next**.

5. Step 3 : _Configure sign-up experience_, leave all default options and click on **Next**.

6. Step 4: _Configure message delivery_, choose **Send email with Cognito** for **Email provider** and leave all other default options then click on **Next**.

7. Step 5: _Integrate your app_, provide the **User pool name** : _**Demo-user-pool**_, **App client name**: _**Dockerdemo-app**_, leave other default options and click Next.

8. Step 6: Review and click on **Create User Pool**. 
![Amazon cognito Demo user pool](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/es48n2tpdzh4o7ntyasd.png)

9. In the details page of the created user pool, click on **App Integration** tab -> **Actions** -> **Create Cognito Domain** and provide the domain name then click **Create Cognito Domain**. 

10. Again, in the **App Integration** tab, navigate to the  **App client list** section and click on **Dockerdemo-app** to preview its details.
![Amazon cognito app client details](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m1b6d41zcxyvqn9ltmjv.png)

11. In the **App client** details, scroll down to the **Pinpoint analytics** section and click on **Edit**, the **Edit Hosted UI** page will open.

12. In the **Edit Hosted UI** page, provide the **Allowed callback URLs** : https://example.com, check **Cognito user pool** in the **Identity providers** section, check all **OpenID Connect scopes** options then click **Save changes**.

13. Back to the **App client** details, the Pinpoint analytics section should look like the image below:
![Amazon cognito App client hosted UI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fblvzua7z90e7tkrqk6l.png)

14. Make sure to save the Cognito domain from the **Domain** section, and the **Client ID** for the created client App from the  **App client list** section.

## Create Spring boot Rest API

In this section, we will create a spring boot Rest API. For demonstration purposes, you can clone the Git hub repository [here](https://github.com/wilkom2009/docker-demo.git) and open it in your favorite IDE.
The idea here is to implement Spring security Rest API authentication with [OAuth 2.0 JWT Bearer Tokens](https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/index.html). 
Instead of implementing the [JWT authentication tokens generation mechanism](https://wilkom2009.hashnode.dev/java-fullstack-backend-rest-api-spring-security-and-jwt-a),  we will use Amazon Cognito to manage it.

1. Provide the needed dependencies in the **pom.xml** file for Spring security OAuth 2.0 support

    ```xml
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <dependency>			 
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-resource- 
 server</artifactId>
    </dependency>
    ```

2. 
In the **application.yaml** file, specify the Authorization Server:

    ```yml
    spring:
      security:
        oauth2:
          resourceserver:
            jwt:
              issuer-uri: https://cognito-idp.[AWS REGION].amazonaws.com/[USER_POOL_ID]
    ```

3. Create a security configuration file.
    ```java
    package com.wilkom.dockerdemo.security;

    import org.springframework.context.annotation.Bean;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.web.SecurityFilterChain;

    @EnableWebSecurity // Enable Spring Security’s web 
      security support
    @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) // To configure method-level security
    public class SecurityConfig {

        @Bean
        public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
 
            http.cors().and().csrf().disable()
                .authorizeRequests(expressionInterceptUrlRegistry -> expressionInterceptUrlRegistry
                        .anyRequest().authenticated())
                .oauth2ResourceServer().jwt();
            return http.build();
        }
    }
    ```
>In the configuration class above, we have made each endpoint accessible only by OAuth 2.0 authenticated requests. That's all we have to do in our API Rest backend.

## Test the Rest API
We will use Postman to test our Rest API.

1\. Signup user into the Amazon Cognito

- 
Open Postman and provide values from Amazon Cognito User provider settings:
    ![Postman Authorization Token generation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4y6r0uj5qvxns0vyyx7.png)

>2/ Callback URL : https://example.com (make sure to provide the exact callback url you set in the Cognito)
>3/ Auth URL : https://xxxxx.auth.us-east-1.amazoncognito.com/login (remember to append /login)
>4/ Access Token URL : https://xxxxx.auth.us-east-1.amazoncognito.com/oauth2/token (remember to append /oauth2/token)
>5/ Client ID : Dockerdemo-app **App Client ID**

- Then click on **Get New Access Token** at the bottom, the Sign-in screen will open, click on Sign up link at the bottom the get the signup screen as follow:
    ![Cognito Sign up screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p5fw3eue1eqpmltiz26x.png)
>Enter user name, valid email and password then click on Sign-up.

- The verification code screen should appear, open the valid email box to get the verification code:
    ![Cognito verification code screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x47j45zhuqiojux21owd.png)

- If the code verification is successful, a token will be generated, click on **Use Token**:
    ![Postman token generated](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ef24wchi4tulhov63sf5.png)


2\. Make API call

- 
Now, you can run the Spring boot app.

- Go to Postman, enter a GET endpoint URL, 

```bash
localhost:8080/api/
```

- 
Make sure the token is in use in the Authorization OAuth 2.0 tab and click on **Send**:

![Postman Get request successful](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uyqemitzvf6khp32bd5d.png)

>PS : In a real project, the Signup and Sign-in processes will be implemented in the front-end apps, please see [this guide](https://aws.amazon.com/cognito/getting-started/) to do so.

As you can see, Amazon Cognito is an amazing AWS service that simplifies Spring boot backend Rest API user management.

Thanks for reading.