AWS Cognito GetCredentialsForIdentity - java

I am trying to use the approach outlined in the following blog article to authenticate a cognito identity to S3 from Java:
https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-authentication-part-4-enhanced-flow/
I have successfully implemented the developer authentication provider and can retrieve a valid OpenId token for my cognito identity. That token works in both the iOS and Android SDKs when uploading/downloading files to S3. Unfortunately, I have not been able to successfully authenticate the same identity to S3 in my Java app. Here is the code I am using:
Map<String, String> logins = new HashMap();
logins.put("cognito-identity.amazonaws.com", cognitoOpenIdToken);
GetCredentialsForIdentityRequest getCredentialsRequest =
new GetCredentialsForIdentityRequest()
.withIdentityId(cognitoIdentityId)
.withLogins(logins);
AmazonCognitoIdentityClient cognitoIdentityClient =
new AmazonCognitoIdentityClient();
GetCredentialsForIdentityResult getCredentialsResult =
cognitoIdentityClient.getCredentialsForIdentity(getCredentialsRequest);
The call to getCredentialsForIdentity fails with the error "com.amazonaws.AmazonClientException: Unable to load AWS credentials from any provider in the chain". This method is documented as a public API that does not require authentication. However, I have noticed that the call succeeds if done from an environment where there are valid IAM credentials configured in the system variables. It fails in environments where that is not the case.
Am I missing something simple?

Have you tried initializing the AmazonCognitoIdentityClient in following manner?
new AmazonCognitoIdentityClient(new AnonymousAWSCredentials());
Otherwise, by design of AWS Java SDK, the service clients look for AWS credentials, because same client can be used for accessing Authenticated APIs as well.

maybe is not that you need to be as another IAM user... but that the user that is running you method has a "deny" on sts:* (or some other actions that you need)

Related

How to authenticate with DefaultAzureCredentials in VS Code with Java?

I am using VS Code to develop an Azure function with Java 11. I am able to authenticate using VisualStudioCodeCredential but when I try to use the DefaultAzureCredential class I get the below error. It is my understanding that for this app to run locally and in the Azure hosted environment that I need the DefaultAzureCredential. Why does this happen and how do I fix it? Is there a better/preferred way to do the authentication?
Caused by: com.azure.core.exception.ClientAuthenticationException: DefaultAzureCredential
authentication failed. ---> IntelliJCredential authentication failed. Error Details:
Unrecognized field "tenantId" (class
com.azure.identity.implementation.IntelliJAuthMethodDetails), not marked as ignorable (4 known
properties: "authMethod", "azureEnv", "accountEmail", "credFilePath"])
Here is my code. The error occurs when I try to get the secret from the vault.
secretClient = new SecretClientBuilder().vaultUrl(System.getenv("KeyVaultURL"))
.credential(new DefaultAzureCredentialBuilder().build()).buildClient();
String secretValue= secretClient.getSecret("secretValue").getValue();
Using VisualStudioCodeCredential works:
secretClient = new SecretClientBuilder().vaultUrl(System.getenv("KeyVaultURL"))
.credential(new VisualStudioCodeCredential().build()).buildClient();
String secretValue= secretClient.getSecret("secretValue").getValue();
Links to some of the docs that I have used for reference.
https://learn.microsoft.com/en-us/java/api/overview/azure/identity-readme?view=azure-java-stable
https://learn.microsoft.com/en-us/azure/developer/java/sdk/identity#key-concepts
https://learn.microsoft.com/en-us/azure/developer/java/sdk/identity-azure-hosted-auth#default-azure-credential
The issue was occurring due to how DefaultAzureCredential chooses the authentication method. During runtime an error occurred because the app was unable to authenticate using IntelliJ authentication even though the app was running from Visual Studio Code.
Default Azure credential
The DefaultAzureCredential is appropriate for most scenarios where the application ultimately runs in the Azure Cloud. DefaultAzureCredential combines credentials that are commonly used to authenticate when deployed, with credentials that are used to authenticate in a development environment. The DefaultAzureCredential will attempt to authenticate via the following mechanisms in order.
Microsoft provides a way to get around this with the ChainedTokenCredential class.
The ChainedTokenCredential class provides the ability to link together multiple credential instances to be tried sequentially when authenticating. The following example demonstrates creating a credential which will attempt to authenticate using managed identity, and fall back to certificate authentication if a managed identity is unavailable in the current environment. This example authenticates an EventHubClient from the azure-eventhubs client library using the ChainedTokenCredential. There's also a compilable sample to create a Key Vault secret client you can copy-paste.
Using the ChainedTokenCredential I was able to change the order to check ManagedIdentityCredential and then VisualStudioCodeCredential.

Embedding AWS IAM credentials in the code with the Java SDK

I am using the Java AWS IoT SDK, and i'm I'm stuck with a problem whereby I have to embed my AWS IAM access key and secret key credentials into my Java application code on my devices.
The credentials are just used initially to create the client in my code, then X.509 certificates are used after for the MQTT authentication and communication. .
I've heard of a way to avoid the need of embedding IAM credentials in the code by using AWSCredentialsProvider with tokens etc. However, I don't see any actual examples of how to achieve this without embedding credentials. Below is a snippet of my code showing how I create the client object using the credentials. Thanks.
String AWS_ACCESSKEY = "AKXXXXXXXXXXXXX"; // not real key
String AWS_SECRETKEY = "ABCXXXXXXXXXXXXXXXXXXXXXX"; // not real key
Regions AWS_REGION = Regions.US_EAST_2;
AWSIot client = AWSIotClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(new
BasicAWSCredentials(AWS_ACCESSKEY, AWS_SECRETKEY))).withRegion(AWS_REGION).build();
You can pass this credentials to normal application.properties file.
You just need to do 2 things.
Create public class AwsCredentials with annotations #ConfigurationProperties and #Configuration.
Pass Your access and secret to application.properties file
You can read more in this tutorial : click
Next when You want to use this properties in builder You need to call it like this:
AWSIot client = AWSIotClientBuilder.standard()
.withCredentials(
new AWSStaticCredentialsProvider(
new BasicAWSCredentials(
this.awsCredentials.getAccessKey(),
this.awsCredentials.getSecretKey()
)
)
)
.withRegion(AWS_REGION)
.build();
PS. You can export region to properties too.
You can use temporary security credentials instead of actual access keys. Do check this link.
https://docs.aws.amazon.com/general/latest/gr/aws-access-keys-best-practices.html
To get credentials to access AWS IoT (or other services) you could get temporary security credentials from Cognito Identity Pool. You can find the simplest way and steps needed to do get credentials here.
Also consider that, to get idToken (JWT) from Cognito user pool and then access and secret token Cognito Identity pool, you need to use AWS Java SDK in your mobile or desktop application. You can find more information about AWS JAVA SDK here and some samples here, here, .

How do I authenticate to Azure Storage from Java using MSI?

I can't figure out how to create a BlobClient, or a StorageAccount reference using a Managed Identity (MI/MSI) in Java. Creating and using the MI to asign RBAC on the storage is straightforward, but I can't work out how to make use of this in my code.
I got this working previously with KeyVault, because I found a lot more examples online of other people doing this, and it was ultimately really simple:
MSICredentials credentials = new MSICredentials(AzureEnvironment.AZURE);
KeyVaultClient kvClient = new KeyVaultClient(credentials);
But I can't find a similar Storage class that will take the MSICredentials as parameter, nor find a way to create StorageCredentials using the MSICredentials.
Has anyone else got this working or have any bright ideas?
There is also a way for Azure Storage Account to authenticate from MSI in Java. Just in a different way from KeyVault.
It also shows in the document Azure AD Authentication for Azure Blobs and Queues now in public preview. There are three ways to authenticate for Azure Storage, see Credential in Azure Storage Java V10 Overview.
You should first get the access token with the MSI and then use TokenCredentials to authenticate. See the way that Getting a token for system assigned identity.

Why do I need to provide AWS Credentials when using the Cognito SDK?

My Java app is using AWS Cognito user pools. It uses the Amazon SDK to sign users in. EG:
Map<String, String> authParams = new HashMap<>();
authParams.put("USERNAME", email);
authParams.put("PASSWORD", StringUtils.SPACE);
AdminInitiateAuthRequest authRequest = new AdminInitiateAuthRequest()
.withAuthFlow(AuthFlowType.ADMIN_NO_SRP_AUTH)
.withAuthParameters(authParams)
.withClientId(clientId)
.withUserPoolId(poolId);
AdminInitiateAuthResult result = cognitoClient.adminInitiateAuth(authRequest);
This code above will not work without AWS access credentials. Why though? I just want to access the AWS Cognito user pool. Is it absolutely required that I need to pass in AWS credentials into the Java app? Trying to avoid having to either version AWS credentials or manually configure them (or set up some auto-configure mechanism).
You're using adminInitiateAuth, which is a server-side authentication flow. This is how your credentialed backend would authenticate a user.
For client-side authentication flow, you should use initiateAuth, not adminInitiateAuth.
See User Pool Authentication Flow in the Developer Guide.

AWS MQTT Websocket, authenticated access being restricted

I'm currently working on an AWS mobile app project in Android Studio, and I'm currently trying to set up a MQTT-connection to AWS IoT through AWS Cognito & IAM authentication.
If I run the MQTT-client as a stand-alone project without authentication, assuming an unauthenticated IAM role, I'm able to connect to MQTT, and subscribe & recieve from the given topic.
However, once I try to run my MQTT-client through an authenticated user (through a log-in on my mobile app), the client is restricted, and constantly tries to reconnect.
I enabled logging in AWS CloudWatch, and the following debug message is given:
"... EVENT:MQTT Client Connect MESSAGE:Connect Status: AUTHORIZATION_ERROR"
The unauth and auth roles in the IAM console have identical policies & resource access, yet something's stopping the MQTT connection.
PS. The Auth role can, in my app, access userfiles & S3, the probelem seems to be unique for IoT access.
Does anyone know if it's possible to allow connections through an authorized user without using cert-files, if so, how? Unless I'm mistaken Cognito and IAM should be able to perform the required authorization to access resources (and it does, as long as I'm not logged in to the auth role)
Appreciate any tips I can get at this point, been struggling for a while.
I eventually figured out the issue by reading the AWS documentation more thoroughly.
The AWS credentials provider does not store the required information to authorize access through AWS. So I solved the issue by retreiving the required user tokens into a hash, and setting these to the credentials provider, and the issue was finally solved.
AuthenticationResultType authenticationResultType = new AuthenticationResultType();
String idToken = authenticationResultType.getIdToken();
// Initialize the AWS Cognito credentials provider
credentialsProvider = new CognitoCachingCredentialsProvider(
getApplicationContext(),
COGNITO_POOL_ID,
MY_REGION
);
Map<String, String> logins = new HashMap<String, String>();
logins.put("yourAWSEndpoint", idToken);
credentialsProvider.setLogins(logins);

Categories