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/)
Spring Security provides comprehensive OAuth 2 support.
After running the Spring Boot Application, go to http://localhost:8080 and click on Login with AWS Cognito. Enter username and password provided by the administrator. Since you are logging in for the first time, you will have to update your password as the password was previously set by the administrator.
After a user logs in, an Amazon Cognito user pool returns a JWT. The JWT is a Base64-encoded JSON string ("claims") that contains information about the user. Amazon Cognito returns three tokens: the ID token, the access token, and the refresh token.
OpenID Connect is an open standard for authentication that a number of login providers support. Amazon Cognito supports you to link identities with OpenID Connect providers that you configure through AWS Identity and Access Management .
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
client-secret
in the spring config)redirect-uri-template
in the spring config)jwk-set-uri
in the spring config)With everything in place, the Spring Boot app will automatically generate a login url
Redirecting you to the cognito login page where you can enter your cognito credentials
And after a successful authentication you'll be able to do a secure REST call
With a REST controller like this :
@RestController
public class ExampleController {
@RequestMapping("/")
public String email(Principal principal) {
return "Hello " + principal.getName();
}
}
We can create Spring Boot resource server, keeping Cognito as Identity Provider.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With