Skip to content

Instantly share code, notes, and snippets.

@alexramos1
Last active July 30, 2023 07:44
Show Gist options
  • Save alexramos1/329f8a927707c266e06bf41219f66570 to your computer and use it in GitHub Desktop.
Save alexramos1/329f8a927707c266e06bf41219f66570 to your computer and use it in GitHub Desktop.
Simplest possible implementation of AWS Cognito username/password authentication on Spring Security.
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.cognitoidp.AWSCognitoIdentityProvider;
import com.amazonaws.services.cognitoidp.AWSCognitoIdentityProviderClientBuilder;
import com.amazonaws.services.cognitoidp.model.AdminInitiateAuthRequest;
import com.amazonaws.services.cognitoidp.model.AdminInitiateAuthResult;
import com.amazonaws.services.cognitoidp.model.AuthFlowType;
@Component
public class CognitoAuthenticationProvider implements AuthenticationProvider {
@Value("${COGNITO_POOL_ID}")
private String poolId;
@Value("${COGNITO_CLIENT_ID}")
private String clientId;
@Value("${COGNITO_CLIENT_SECRET}")
private String clientSecret;
private DefaultAWSCredentialsProviderChain awsCredentialsProvider = new DefaultAWSCredentialsProviderChain();
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String userId = authentication.getName();
String password = authentication.getCredentials().toString();
Map<String, String> params = new HashMap<>();
params.put("USERNAME", userId);
params.put("SECRET_HASH", calculateSecretHash(userId));
params.put("PASSWORD", password);
AdminInitiateAuthRequest request = new AdminInitiateAuthRequest()
.withUserPoolId(poolId)
.withClientId(clientId)
.withAuthFlow(AuthFlowType.ADMIN_NO_SRP_AUTH)
.withAuthParameters(params);
AWSCognitoIdentityProvider identityProvider = AWSCognitoIdentityProviderClientBuilder.standard()
.withCredentials(awsCredentialsProvider).withRegion(Regions.US_WEST_2).build();
AdminInitiateAuthResult result = identityProvider.adminInitiateAuth(request);
if(result.getChallengeName().equals("NEW_PASSWORD_REQUIRED")) {
// ignore
}
return new UsernamePasswordAuthenticationToken(
userId, password, new ArrayList<>());
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
private String calculateSecretHash(@Nonnull String userName) {
SecretKeySpec signingKey = new SecretKeySpec(clientSecret.getBytes(StandardCharsets.UTF_8),
HmacAlgorithms.HMAC_SHA_256.toString());
try {
Mac mac = Mac.getInstance(HmacAlgorithms.HMAC_SHA_256.toString());
mac.init(signingKey);
mac.update(userName.getBytes(StandardCharsets.UTF_8));
byte[] rawHmac = mac.doFinal(clientId.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(rawHmac);
} catch (Exception ex) {
throw new RuntimeException("Error calculating secret hash", ex);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment