17

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/)

Thales Minussi
  • 6,965
  • 1
  • 30
  • 48
jalmaraz
  • 201
  • 1
  • 2
  • 10

3 Answers3

33

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();
    }

}
ddewaele
  • 22,363
  • 10
  • 69
  • 82
  • I struggled as well trying to create these things not knowing it's all handled in spring. As for a Cognito resource that helped me the most was: https://github.com/aws-quickstart/saas-identity-cognito – Switcher May 13 '19 at 14:36
  • 1
    How are you managing logout? I have spring keeping automatically logging-in after logout, so users are not being logged out – kappa Jun 25 '19 at 16:00
  • Trying to solve the same problem Kappa. Check out this: https://docs.aws.amazon.com/pt_br/cognito/latest/developerguide/logout-endpoint.html – Alexandre Mucci Jun 25 '19 at 17:27
  • 1
    @AlexandreMucci thank you for the hint, I have already read the logout endpoint doc, but it seems that spring security is not invoking such endpoint when logging out before invalidating HTTP session and deleting the cookies; so my user is not being actually logged out. Moreover what It seems very strange to me is that having no cookie, no local storage data and no other header params in next request automatically log me in again (but only from same tab of same browser) – kappa Jun 26 '19 at 05:55
  • @kappa I'm have the same issue. Were you able to get the logout working successfully? If so, would you please provide some detail? – Stephen Gibson Nov 14 '19 at 17:22
  • I'm getting a `java.lang.IllegalStateException: Unknown provider ID 'cognito'` on startup. – Double M Apr 25 '20 at 09:58
  • can you please go in detail on the `provider.cognito` configuration and help map it to AWS console? – user3833308 Jun 24 '20 at 06:44
  • With spring boot version 2.3.2.RELEASE, I had to rename redirect-uri-template to redirect-uri – krishnakumarp Aug 14 '20 at 17:55
  • We heard this use case, and will be adding it to the official AWS Doc repo soon. – smac2020 Nov 20 '20 at 19:57
  • 1
    @smac2020 are you done? If yes, can you please share the offical docs? – Muhammad Waqas Dilawar Aug 27 '21 at 05:14
  • see link below to an example – smac2020 Aug 27 '21 at 12:49
  • A user deleted the link to the Amazon Example - not sure why. Here is the link - https://github.com/awsdocs/aws-doc-sdk-examples/tree/master/javav2/usecases/creating_amazon_cognito_app – smac2020 Aug 27 '21 at 13:09
6

We can create Spring Boot resource server, keeping Cognito as Identity Provider.

Spring boot Resource Server

Dependency:

    <!--  Spring Security-->
    <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>

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

Spring Security Configuration:

EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class OAuth2ResourceServerSecurityConfiguration extends ResourceServerConfigurerAdapter {

  private final ResourceServerProperties resource;

  public OAuth2ResourceServerSecurityConfiguration(ResourceServerProperties resource) {
    this.resource = resource;
  }

  @Override
  public void configure(HttpSecurity http) throws Exception {

    http.cors();

    http.csrf().disable();

    http.authorizeRequests()
        .antMatchers("/api/public/**").permitAll()
        .antMatchers("/actuator/health").permitAll()
        .anyRequest().authenticated();
  }


  // Note: Cognito Converter
  @Bean
  public TokenStore jwkTokenStore() {
    return new JwkTokenStore(
        Collections.singletonList(resource.getJwk().getKeySetUri()),
        new CognitoAccessTokenConverter(),
        null);
  }
}

Cognito Access Token Converter:

Here we are converting the Cognito claims to Spring Security consumable format.

@Component
public class CognitoAccessTokenConverter extends JwtAccessTokenConverter {

  // Note: This the core part.
  private static final String COGNITO_GROUPS = "cognito:groups";
  private static final String SPRING_AUTHORITIES = "authorities";
  private static final String COGNITO_USERNAME = "username";
  private static final String SPRING_USER_NAME = "user_name";

  @SuppressWarnings("unchecked")
  @Override
  public OAuth2Authentication extractAuthentication(Map<String, ?> claims) {

    if (claims.containsKey(COGNITO_GROUPS))
      ((Map<String, Object>) claims).put(SPRING_AUTHORITIES, claims.get(COGNITO_GROUPS));
    if (claims.containsKey(COGNITO_USERNAME))
      ((Map<String, Object>) claims).put(SPRING_USER_NAME, claims.get(COGNITO_USERNAME));
    return super.extractAuthentication(claims);
  }
}

application.properties

server:
  port: 8081
security:
  oauth2:
    resource:
      userInfoUri: https://<cognito>.auth.eu-west-1.amazoncognito.com/oauth2/userInfo
      tokenInfoUri: https://<cognito>.auth.eu-west-1.amazoncognito.com/oauth2/token
      jwk:
        key-set-uri: https://cognito-idp.<region>.amazonaws.com/<user-pool-id>/.well-known/jwks.json
    client:
      clientId: <client-id>

For complete article, refer: Integrate Spring Boot Resource Server with Cognito Identity Provider

Arjun Sunil Kumar
  • 1,781
  • 3
  • 28
  • 46
  • I like that solution but `ResourceServerProperties` is deprecated in spring... – Louis-wht Feb 22 '20 at 18:08
  • These days, REST API's are usually hosted via API-Gateway, which has Cognito integration. – Arjun Sunil Kumar Feb 22 '20 at 18:38
  • Interesting, I didn't thought about that option altough I planned on using API-GW. Will look at that one. However, doesn't solve the deprecated issue :) – Louis-wht Feb 22 '20 at 18:42
  • @Louis-wht, could you share any reference link, stating this deprecation. I see that `spring-boot-starter-oauth2-resource-server` has the latest build pushed on Jan 2020. – Arjun Sunil Kumar Feb 22 '20 at 18:42
  • I'm using 2.2.3:RELEASE version and when opening the file in Intellij, the doc states. Can't find that version of the doc online however... `@deprecated See the OAuth 2.0 Migration Guide for Spring Security 5.` – Louis-wht Feb 22 '20 at 18:55
  • 1
    The core of this answer was to point out `CognitoAccessTokenConverter`, which convert `Cognito claims` to `spring-security` format. I assume, after migrating, you might still need the same solution for cognito integration. – Arjun Sunil Kumar Feb 22 '20 at 19:21
-1

There is a specific example at the Baeldung site on how to do this

https://www.baeldung.com/spring-security-oauth-cognito

EvilJinious1
  • 2,773
  • 6
  • 43
  • 63