Skip to content

Commit 53feeb3

Browse files
committed
awspringgh-1246: AWS Cognito Integration 1.0
1 parent d884098 commit 53feeb3

File tree

8 files changed

+324
-2
lines changed

8 files changed

+324
-2
lines changed

spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cognito/CognitoAutoConfiguration.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ public CognitoIdentityProviderClient cognitoIdentityProviderClient(CognitoProper
6060

6161
@Bean
6262
@ConditionalOnMissingBean
63-
@ConditionalOnProperty(name = {"spring.cloud.aws.cognito.clientId", "spring.cloud.aws.cognito.userPoolId"})
63+
@ConditionalOnProperty(name = { "spring.cloud.aws.cognito.client-id", "spring.cloud.aws.cognito.user-pool-id" })
6464
public CognitoTemplate cognitoTemplate(CognitoProperties cognitoProperties,
6565
CognitoIdentityProviderClient cognitoIdentityProviderClient) {
6666
return new CognitoTemplate(cognitoIdentityProviderClient, cognitoProperties.getClientId(),
67-
cognitoProperties.getUserPoolId());
67+
cognitoProperties.getUserPoolId(), cognitoProperties.getClientSecret());
6868
}
6969
}

spring-cloud-aws-autoconfigure/src/main/java/io/awspring/cloud/autoconfigure/cognito/CognitoProperties.java

+7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
import io.awspring.cloud.autoconfigure.AwsClientProperties;
1919
import org.springframework.boot.context.properties.ConfigurationProperties;
2020

21+
/**
22+
* Configuration properties for AWS Cognito Integration
23+
*
24+
* @author Oleh Onufryk
25+
* @since 3.3.0
26+
*/
27+
2128
@ConfigurationProperties(CognitoProperties.CONFIG_PREFIX)
2229
public class CognitoProperties extends AwsClientProperties {
2330

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2013-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.awspring.cloud.cognito;
17+
18+
import java.util.List;
19+
import software.amazon.awssdk.services.cognitoidentityprovider.model.AdminCreateUserResponse;
20+
import software.amazon.awssdk.services.cognitoidentityprovider.model.AdminInitiateAuthResponse;
21+
import software.amazon.awssdk.services.cognitoidentityprovider.model.AttributeType;
22+
import software.amazon.awssdk.services.cognitoidentityprovider.model.ConfirmForgotPasswordResponse;
23+
import software.amazon.awssdk.services.cognitoidentityprovider.model.ForgotPasswordResponse;
24+
import software.amazon.awssdk.services.cognitoidentityprovider.model.RespondToAuthChallengeResponse;
25+
26+
/**
27+
* An Interface for the most common Cognito auth operations
28+
*
29+
* @author Oleh Onufryk
30+
* @since 3.3.0
31+
*/
32+
33+
public interface CognitoAuthOperations {
34+
35+
/**
36+
* Logs in a user using username and password
37+
* @param username - the username
38+
* @param password - the password
39+
* @return {@link AdminInitiateAuthResponse} a result of login operation from the AWS Cognito
40+
*/
41+
AdminInitiateAuthResponse login(String username, String password);
42+
43+
/**
44+
* Creates a new user with provided attributes
45+
* @param username - the username
46+
* @param attributeTypes - the list of user attributes defined by user pool
47+
* @return {@link AdminCreateUserResponse} a result of user creation operation from the AWS Cognito
48+
*/
49+
AdminCreateUserResponse createUser(String username, List<AttributeType> attributeTypes);
50+
51+
/**
52+
* Resets password for a user
53+
* @param username - the username
54+
* @return {@link ForgotPasswordResponse} a result of password reset operation from the AWS Cognito
55+
*/
56+
ForgotPasswordResponse resetPassword(String username);
57+
58+
/**
59+
* Confirms password reset
60+
* @param username - the username
61+
* @param confirmationCode - the confirmation code for password reset operation
62+
* @param newPassword - the new password
63+
* @return {@link ConfirmForgotPasswordResponse} a result of password reset confirmation operation from the AWS
64+
* Cognito
65+
*/
66+
ConfirmForgotPasswordResponse confirmResetPassword(String username, String confirmationCode, String newPassword);
67+
68+
/**
69+
* Sets a permanent password for a new user
70+
* @param session - the session id returned by the login operation
71+
* @param username - the username of the user
72+
* @param password - the permanent password for user's account
73+
* @return {@link RespondToAuthChallengeResponse} a result of setting permanent password operation from the AWS
74+
* Cognito
75+
*/
76+
RespondToAuthChallengeResponse setPermanentPassword(String session, String username, String password);
77+
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2013-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.awspring.cloud.cognito;
17+
18+
/**
19+
* Parameters used in AWS Cognito operations.
20+
*
21+
* @author Oleh Onufryk
22+
* @since 3.3.0
23+
*/
24+
25+
public final class CognitoParameters {
26+
27+
private CognitoParameters() {
28+
}
29+
30+
/**
31+
* Parameter represents username for a user.
32+
*/
33+
public static final String USERNAME_PARAM_NAME = "USERNAME";
34+
35+
/**
36+
* Parameter represents password for a user.
37+
*/
38+
public static final String PASSWORD_PARAM_NAME = "PASSWORD";
39+
40+
/**
41+
* Parameter represents a compute secret hash for a user.
42+
*/
43+
public static final String SECRET_HASH_PARAM_NAME = "SECRET_HASH";
44+
45+
/**
46+
* Parameter represents a new password for a user.
47+
*/
48+
public static final String NEW_PASSWORD_PARAM_NAME = "NEW_PASSWORD";
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright 2013-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.awspring.cloud.cognito;
17+
18+
import java.util.List;
19+
import java.util.Map;
20+
import org.springframework.util.Assert;
21+
import software.amazon.awssdk.services.cognitoidentityprovider.CognitoIdentityProviderClient;
22+
import software.amazon.awssdk.services.cognitoidentityprovider.model.AdminCreateUserRequest;
23+
import software.amazon.awssdk.services.cognitoidentityprovider.model.AdminCreateUserResponse;
24+
import software.amazon.awssdk.services.cognitoidentityprovider.model.AdminInitiateAuthRequest;
25+
import software.amazon.awssdk.services.cognitoidentityprovider.model.AdminInitiateAuthResponse;
26+
import software.amazon.awssdk.services.cognitoidentityprovider.model.AttributeType;
27+
import software.amazon.awssdk.services.cognitoidentityprovider.model.AuthFlowType;
28+
import software.amazon.awssdk.services.cognitoidentityprovider.model.ChallengeNameType;
29+
import software.amazon.awssdk.services.cognitoidentityprovider.model.ConfirmForgotPasswordRequest;
30+
import software.amazon.awssdk.services.cognitoidentityprovider.model.ConfirmForgotPasswordResponse;
31+
import software.amazon.awssdk.services.cognitoidentityprovider.model.ForgotPasswordRequest;
32+
import software.amazon.awssdk.services.cognitoidentityprovider.model.ForgotPasswordResponse;
33+
import software.amazon.awssdk.services.cognitoidentityprovider.model.RespondToAuthChallengeRequest;
34+
import software.amazon.awssdk.services.cognitoidentityprovider.model.RespondToAuthChallengeResponse;
35+
36+
/**
37+
* Higher level abstraction over {@link CognitoIdentityProviderClient} providing methods for the most common auth
38+
* operations
39+
*
40+
* @author Oleh Onufryk
41+
* @since 3.3.0
42+
*/
43+
44+
public class CognitoTemplate implements CognitoAuthOperations {
45+
46+
private final CognitoIdentityProviderClient cognitoIdentityProviderClient;
47+
private final String clientId;
48+
private final String userPoolId;
49+
private final String clientSecret;
50+
51+
public CognitoTemplate(CognitoIdentityProviderClient cognitoIdentityProviderClient, String clientId,
52+
String userPoolId, String clientSecret) {
53+
Assert.notNull(cognitoIdentityProviderClient, "cognitoIdentityProviderClient is required");
54+
Assert.notNull(clientId, "clientId is required");
55+
Assert.notNull(userPoolId, "userPoolId is required");
56+
this.cognitoIdentityProviderClient = cognitoIdentityProviderClient;
57+
this.clientId = clientId;
58+
this.userPoolId = userPoolId;
59+
this.clientSecret = clientSecret;
60+
}
61+
62+
@Override
63+
public AdminInitiateAuthResponse login(String username, String password) {
64+
AdminInitiateAuthRequest adminInitiateAuthRequest = AdminInitiateAuthRequest.builder().userPoolId(userPoolId)
65+
.clientId(clientId).authFlow(AuthFlowType.ADMIN_USER_PASSWORD_AUTH)
66+
.authParameters(resolveAuthParameters(username, password)).build();
67+
return cognitoIdentityProviderClient.adminInitiateAuth(adminInitiateAuthRequest);
68+
}
69+
70+
@Override
71+
public AdminCreateUserResponse createUser(String username, List<AttributeType> attributeTypes) {
72+
AdminCreateUserRequest createUserRequest = AdminCreateUserRequest.builder().userPoolId(userPoolId)
73+
.username(username).userAttributes(attributeTypes).build();
74+
return cognitoIdentityProviderClient.adminCreateUser(createUserRequest);
75+
}
76+
77+
@Override
78+
public ForgotPasswordResponse resetPassword(String username) {
79+
ForgotPasswordRequest forgotPasswordRequest = ForgotPasswordRequest.builder().clientId(clientId)
80+
.username(username).build();
81+
82+
return cognitoIdentityProviderClient.forgotPassword(forgotPasswordRequest);
83+
}
84+
85+
@Override
86+
public ConfirmForgotPasswordResponse confirmResetPassword(String username, String confirmationCode,
87+
String newPassword) {
88+
ConfirmForgotPasswordRequest confirmForgotPasswordRequest = ConfirmForgotPasswordRequest.builder()
89+
.clientId(clientId).username(username).password(newPassword).confirmationCode(confirmationCode)
90+
.secretHash(CognitoUtils.calculateSecretHash(clientId, clientSecret, username)).build();
91+
return cognitoIdentityProviderClient.confirmForgotPassword(confirmForgotPasswordRequest);
92+
}
93+
94+
@Override
95+
public RespondToAuthChallengeResponse setPermanentPassword(String session, String username, String password) {
96+
RespondToAuthChallengeRequest respondToAuthChallengeRequest = RespondToAuthChallengeRequest.builder()
97+
.clientId(clientId).challengeName(ChallengeNameType.NEW_PASSWORD_REQUIRED)
98+
.challengeResponses(Map.of(CognitoParameters.USERNAME_PARAM_NAME, username,
99+
CognitoParameters.NEW_PASSWORD_PARAM_NAME, password, CognitoParameters.SECRET_HASH_PARAM_NAME,
100+
CognitoUtils.calculateSecretHash(clientId, clientSecret, username)))
101+
.build();
102+
return cognitoIdentityProviderClient.respondToAuthChallenge(respondToAuthChallengeRequest);
103+
}
104+
105+
private Map<String, String> resolveAuthParameters(String username, String password) {
106+
return Map.of(CognitoParameters.USERNAME_PARAM_NAME, username, CognitoParameters.PASSWORD_PARAM_NAME, password,
107+
CognitoParameters.SECRET_HASH_PARAM_NAME,
108+
CognitoUtils.calculateSecretHash(clientId, clientSecret, username));
109+
}
110+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2013-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.awspring.cloud.cognito;
17+
18+
import java.nio.charset.StandardCharsets;
19+
import java.util.Base64;
20+
import javax.crypto.Mac;
21+
import javax.crypto.spec.SecretKeySpec;
22+
23+
/**
24+
* Utility class for Cognito operations.
25+
*
26+
* @author Oleh Onufryk
27+
* @since 3.3.0
28+
*/
29+
public class CognitoUtils {
30+
31+
private CognitoUtils() {
32+
}
33+
34+
// https://docs.aws.amazon.com/cognito/latest/developerguide/signing-up-users-in-your-app.html#cognito-user-pools-computing-secret-hash
35+
public static String calculateSecretHash(String userPoolClientId, String userPoolClientSecret, String userName) {
36+
final String HMAC_SHA256_ALGORITHM = "HmacSHA256";
37+
SecretKeySpec signingKey = new SecretKeySpec(userPoolClientSecret.getBytes(StandardCharsets.UTF_8),
38+
HMAC_SHA256_ALGORITHM);
39+
try {
40+
Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
41+
mac.init(signingKey);
42+
mac.update(userName.getBytes(StandardCharsets.UTF_8));
43+
byte[] rawHmac = mac.doFinal(userPoolClientId.getBytes(StandardCharsets.UTF_8));
44+
return Base64.getEncoder().encodeToString(rawHmac);
45+
}
46+
catch (Exception e) {
47+
throw new RuntimeException("Error while calculating secret hash for " + userName);
48+
}
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright 2013-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/**
18+
* AWS Cognito integration.
19+
*/
20+
@org.springframework.lang.NonNullApi
21+
@org.springframework.lang.NonNullFields
22+
package io.awspring.cloud.cognito;

spring-cloud-aws-dependencies/pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,12 @@
220220
<version>${project.version}</version>
221221
</dependency>
222222

223+
<dependency>
224+
<groupId>io.awspring.cloud</groupId>
225+
<artifactId>spring-cloud-aws-starter-cognito</artifactId>
226+
<version>${project.version}</version>
227+
</dependency>
228+
223229
<dependency>
224230
<groupId>io.awspring.cloud</groupId>
225231
<artifactId>spring-cloud-aws-test</artifactId>

0 commit comments

Comments
 (0)