Amazon Cognito Oauth2 with Spring Security

jalmaraz picture jalmaraz · Jan 18, 2018 · Viewed 13.4k times · Source

I'm trying to implement Spring Security in a resource server with "Cognito Oauth2", however I don't seem to find too much info. about it (or if It's even possible to do so).

My nearest approach was using "Nimbus+JOSE" to check the validity of the "Access Token" with the "JWKS" and give permissions to acccess the resource. (Similar to the example they give with the "API Gateway Resource Protection Implementation" found here: https://aws.amazon.com/es/blogs/mobile/integrating-amazon-cognito-user-pools-with-api-gateway/)

Answer

ddewaele picture ddewaele · Jul 28, 2018

A great starting point for Oauth2 using the latest Sprint Boot 2.x / Sprint Security 5.x can be found here : https://spring.io/blog/2018/03/06/using-spring-security-5-to-integrate-with-oauth-2-secured-services-such-as-facebook-and-github

It uses Facebook / Github as an example but you can apply it to AWS Cognito also.

This is by far the easiest way to setup a secure REST backend with Spring Security / Cognito OAuth2. Your backend will be secured via Spring Security, and AWS Cognito will be used as the identity provider.

You can setup a vanilla spring boot app using the spring security starter as outlined in the article using the following dependencies :

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
    </dependency>

    <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 provide your cognito configuration (client registration + provider definition) like this :

spring:
  security:
    oauth2:
      client:
        registration:
          cognito-client-1:
            client-id: 391uhnjlr8v8kicm3cru6g1s8g
            client-secret: xxxxxxxxxxxxxxxxxxxxxxxxxx
            client-name: Cognito Code Grant
            provider: cognito
            scope: openid
            redirect-uri-template: http://localhost:8080/login/oauth2/code/cognito
            authorization-grant-type: authorization_code
        provider:
          cognito:
            authorization-uri: https://custom-domain.auth.eu-central-1.amazoncognito.com/oauth2/authorize
            token-uri: https://custom-domain.auth.eu-central-1.amazoncognito.com/oauth2/token
            user-info-uri: https://custom-domain.auth.eu-central-1.amazoncognito.com/oauth2/userInfo
            jwk-set-uri: https://cognito-idp.eu-central-1.amazonaws.com/eu-central-1_xxxxxxxxx/.well-known/jwks.json
            user-name-attribute: cognito:username

As far as Cognito is concerned you need to have a user pool / identity pool with a couple of users and a valid app client ( = client-id in spring config) in cognito with

  • a secret ( = client-secret in the spring config)
  • the correct grants and scopes (in this case I'm using the authorization_code grant with an openid scope)
  • the correct redirect callback ( = redirect-uri-template in the spring config)
  • a domain configuration in cognito
  • a JWK uri containing your cognito user pool (jwk-set-uri in the spring config)

enter image description here

With everything in place, the Spring Boot app will automatically generate a login url

enter image description here

Redirecting you to the cognito login page where you can enter your cognito credentials

enter image description here

And after a successful authentication you'll be able to do a secure REST call

enter image description here

With a REST controller like this :

@RestController
public class ExampleController {

    @RequestMapping("/")
    public String email(Principal principal) {
        return "Hello " + principal.getName();
    }

}