How to manage secret rotation used by spring boot app running on ECS in AWS cloud - amazon-web-services

My organization is running spring boot app on AWS ECS docker container which reads the credentials for Postgres sql from secrets manager in AWS during boot up. AS part of security complaince, we are rotating the secrets every 3 months. The spring boot app is loosing connection with the database and going down when the RDS credentials are rotated.we have to restart it in order to pick the new credentials to work properly. Is there any way I can read the credentials automatically once the credentials are rotated to avoid restarting the application manually?

After some research I found that the postgres database in AWS supports passwordless authentication using IAM roles. We can generate a token which is valid for 15 mins and can connect to database using that token. I prefer this way of connecting to database rather than using password for my database. More details about setting up password less authentication can be found here
Code example as below
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.services.rds.auth.GetIamAuthTokenRequest;
import com.amazonaws.services.rds.auth.RdsIamAuthTokenGenerator;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.net.URISyntaxException;
import java.sql.SQLException;
import java.util.Properties;
public class RdsIamAuthDataSource extends org.apache.tomcat.jdbc.pool.DataSource {
private static final Logger LOGGER = LoggerFactory.getLogger(RdsIamAuthDataSource.class);
private static final int DEFAULT_PORT = 5432;
private static final String USESSL = "useSSL";
private static final String REQUIRE_SSL = "requireSSL";
private static final String BOOLEAN_TRUE = "true";
private static final String VERIFY_SERVER_CERTIFICATE = "verifyServerCertificate";
private static final String THREAD_NAME = "RdsIamAuthDataSourceTokenThread";
/**
* Constructor for RdsIamAuthDataSource.
* #param props {#link PoolConfiguration}
*/
public RdsIamAuthDataSource(PoolConfiguration props) {
this.poolProperties = props;
}
#Override
public ConnectionPool createPool() throws SQLException {
if (pool == null) {
return createPoolImpl();
} else {
return pool;
}
}
protected ConnectionPool createPoolImpl() throws SQLException {
synchronized (this) {
return pool = new RdsIamAuthConnectionPool(poolProperties);
}
}
private class RdsIamAuthConnectionPool extends ConnectionPool implements Runnable {
private RdsIamAuthTokenGenerator rdsIamAuthTokenGenerator;
private String host;
private String region;
private int port;
private String username;
private Thread tokenThread;
/**
* Constructor for RdsIamAuthConnectionPool.
* #param prop {#link PoolConfiguration}
* #throws SQLException {#link SQLException}
*/
public RdsIamAuthConnectionPool(PoolConfiguration prop) throws SQLException {
super(prop);
}
#Override
protected void init(PoolConfiguration prop) throws SQLException {
try {
final URI uri = new URI(prop.getUrl().substring(5));
this.host = uri.getHost();
this.port = uri.getPort();
if (this.port < 0) {
this.port = DEFAULT_PORT;
}
this.region = StringUtils.split(this.host,'.')[2];
this.username = prop.getUsername();
this.rdsIamAuthTokenGenerator = RdsIamAuthTokenGenerator.builder()
.credentials(new DefaultAWSCredentialsProviderChain())
.region(this.region)
.build();
updatePassword(prop);
final Properties props = prop.getDbProperties();
props.setProperty(USESSL, BOOLEAN_TRUE);
props.setProperty(REQUIRE_SSL, BOOLEAN_TRUE);
props.setProperty(VERIFY_SERVER_CERTIFICATE, BOOLEAN_TRUE);
super.init(prop);
this.tokenThread = new Thread(this, THREAD_NAME);
this.tokenThread.setDaemon(true);
this.tokenThread.start();
} catch (URISyntaxException e) {
LOGGER.error("Database URL is not correct. Please verify", e);
throw new RuntimeException(e.getMessage());
}
}
/**
* Refresh the token every 12 minutes.
*/
#Override
public void run() {
try {
while (this.tokenThread != null) {
Thread.sleep(12 * 60 * 1000);
updatePassword(getPoolProperties());
}
} catch (InterruptedException e) {
LOGGER.error("Background token thread interrupted", e);
}
}
#Override
protected void close(boolean force) {
super.close(force);
final Thread thread = tokenThread;
if (thread != null) {
thread.interrupt();
}
}
private void updatePassword(PoolConfiguration props) {
final String token = rdsIamAuthTokenGenerator.getAuthToken(GetIamAuthTokenRequest.builder()
.hostname(host)
.port(port)
.userName(this.username)
.build());
LOGGER.info("Updated IAM token for connection pool");
props.setPassword(token);
}
}
}
Supply the following DataSource as a spring bean. That's it. Now your application will automatically refresh credentials every 12 minutes
#Bean
public DataSource dataSource() {
final PoolConfiguration props = new PoolProperties();
props.setUrl("jdbc:postgresql://myapp.us-east-2.rds.amazonaws.com/myschema?ssl=true");
props.setUsername("rdsadminuser");
props.setDriverClassName("org.somedatabase.Driver");
return new RdsIamAuthDataSource(props);
}

Related

Testing an API endpoint with Lambda + API Gateway

I'm trying to create and test an API endpoint using AWS Lambda and API Gateway. I can test my function successfully using Lambda Test, but when I try to test my endpoint it gives:
{
"message": "Internal server error"
}
This is my handler class:
package com.amazonaws.lambda.gandhi.conversion.api;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.RandomStringUtils;
import com.amazonaws.lambda.gandhi.conversion.api.Response.AuthClientCredentialResponse;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.lambda.gandhi.conversion.api.utils.ClientAuthPOJO;
public class AuthClientCredentialServiceHandler implements RequestHandler<ClientAuthPOJO, Object> {
private AuthClientCredentialResponse authClientCredentialResponse;
private static final SecureRandom RANDOM = new SecureRandom();
public static int MAX_CLIENT_KEY = 10;
public static int CLIENT_SECRET_LENGTH = 69;
#Override
public AuthClientCredentialResponse handleRequest(ClientAuthPOJO clientIdSecret, Context context) {
String clientSecret;
try {
context.getLogger().log("Input: "
+ clientIdSecret);
String clientId = clientIdSecret.getClientId();
clientSecret = generateClientSecretKey();
Map<String, String> clientCredsMap = getClientCredentials();
if (clientCredsMap.size() > MAX_CLIENT_KEY) {
throw new RuntimeException(String.format("Max limit is %d, Please delete some keys", MAX_CLIENT_KEY));
}
clientCredsMap.forEach((k, v) -> {
if (clientId.equals(k)) {
throw new RuntimeException("Client Already exists");
}
});
storeClientCredentials(clientId, clientSecret);
AuthClientCredentialResponse authClientCredentialResponse = AuthClientCredentialResponse.builder().success(
true).clientId(clientId).clientSecret(clientSecret).build();
this.authClientCredentialResponse = authClientCredentialResponse;
} catch (Exception e) {
throw new RuntimeException(
"Failed to generate client secret: "
+ e.getMessage());
}
return authClientCredentialResponse;
}
private String generateClientSecretKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
String clientSecret = RandomStringUtils.randomAlphanumeric(CLIENT_SECRET_LENGTH);
System.out.printf("clientSecret: %s%n", clientSecret);
return clientSecret;
}
private void storeClientCredentials(String clientId, String clientSecret) throws IOException {
/*
* TODO:
* Some logic to store clientCredentials to a file or DB. Decide later.
*/
System.out.println("temp ClientCredentials stored");
}
public Map<String, String> getClientCredentials() throws IOException {
/*
* TODO:
* Some logic to fetch clientCredentials from file or DB. Decide later.
*/
Map<String, String> clientCredMap = new HashMap<String, String>();
clientCredMap.put("1", "secretKey1");
clientCredMap.put("2", "secretKey2");
clientCredMap.put("3", "secretKey3");
clientCredMap.put("4", "secretKey4");
return clientCredMap;
}
}
My input class:
package com.amazonaws.lambda.gandhi.conversion.api.utils;
public class ClientAuthPOJO {
String clientId;
String clientSecret;
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getClientSecret() {
return clientSecret;
}
public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
}
public ClientAuthPOJO(String clientId, String clientSecret) {
super();
this.clientId = clientId;
this.clientSecret = clientSecret;
}
public ClientAuthPOJO() {
}
}
My test object in lambda:
My test for endpoint in API Gateway:
Can someone please help me figure out the problem in creating the function or API Gateway?
Edit:
When I check the logs, I found that the parameters to the functions (clientId and clientSecret) are null. So there seems to be some problem in the way I'm sending my request body.

Use KCL 1.* in Lambda: Credentials

Using the examples provided in https://github.com/aws/aws-sdk-java/tree/master/src/samples/AmazonKinesis I understand how to use the KCL to read KinesisEvents from my local machine.
I am trying to implement the same logic in Lambda Functions.
To set up the KinesisClientLibConfiguration, you need to provide an AWSCredentialsProvider.
Where do I get these AWSCredentials to create the kinesisClientLibConfiguration in a Lambda?
KinesisClientLibConfiguration kinesisClientLibConfiguration = new KinesisClientLibConfiguration(SAMPLE_APPLICATION_NAME, SAMPLE_APPLICATION_STREAM_NAME, credentialsProvider, workerId);
The full code of the handler looks like this:
public class ProcessKinesisRecords implements RequestHandler<KinesisEvent, Void> {
public static final String SAMPLE_APPLICATION_STREAM_NAME = "kinesis-s";
public static final String SAMPLE_APPLICATION_STREAM_REGION = "eu-west-1";
private static final String SAMPLE_APPLICATION_NAME = "SampleKinesisLambdaApplication";
private static final InitialPositionInStream SAMPLE_APPLICATION_INITIAL_POSITION_IN_STREAM =
InitialPositionInStream.LATEST;
private static ProfileCredentialsProvider credentialsProvider;
public Void handleRequest(KinesisEvent event, Context context) {
init();
int exitCode = 0;
try {
String workerId = InetAddress.getLocalHost().getCanonicalHostName() + ":" + UUID.randomUUID();
KinesisClientLibConfiguration kinesisClientLibConfiguration =
new KinesisClientLibConfiguration(SAMPLE_APPLICATION_NAME,
SAMPLE_APPLICATION_STREAM_NAME,
credentialsProvider,
workerId);
kinesisClientLibConfiguration.withInitialPositionInStream(SAMPLE_APPLICATION_INITIAL_POSITION_IN_STREAM);
kinesisClientLibConfiguration.withRegionName(SAMPLE_APPLICATION_STREAM_REGION);
IRecordProcessorFactory recordProcessorFactory = new AmazonKinesisApplicationRecordProcessorFactory();
Worker worker = new Worker(recordProcessorFactory, kinesisClientLibConfiguration);
System.out.printf("Running %s to process stream %s as worker %s...\n",
SAMPLE_APPLICATION_NAME,
SAMPLE_APPLICATION_STREAM_NAME,
workerId);
worker.run();
} catch (Throwable e) {
System.err.println("Caught throwable while processing data.");
e.printStackTrace();
}
System.exit(exitCode);
return null;
}
private static void init() {
// Ensure the JVM will refresh the cached IP values of AWS resources (e.g. service endpoints).
java.security.Security.setProperty("networkaddress.cache.ttl", "60");
credentialsProvider = new ProfileCredentialsProvider();
try {
credentialsProvider.getCredentials();
} catch (Exception e) {
throw new AmazonClientException("Cannot load the credentials", e);
}
}
}
Lambda does provide environment variables for the credentials:
https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html#lambda-environment-variables
Access them via the EnvironmentVariableCredentialsProvider:
https://github.com/aws/aws-sdk-java/blob/master/aws-java-sdk-core/src/main/java/com/amazonaws/auth/EnvironmentVariableCredentialsProvider.java

WSO2 Identity Server programmatically creating an application throwing 'Illegal Access Attempt' warning

I am developing a Java client which will create an application in WSO2 Identity Server through calling the OAuthAdminService. After some digging I found that registerOAuthApplicationData() method is the one used for creating an application in IS. Before calling the method, I have authenticated the admin user via login() method of AuthenticationAdminStub type. Even after such authentication the registerOAuthApplicationData() method make the IS console to print
[2016-04-26 13:08:52,577] WARN
{org.wso2.carbon.server.admin.module.handler.AuthenticationHandler} -
Illegal access attempt at [2016-04-26 13:08:52,0577] from IP address
127.0.0.1 while trying to authenticate access to service OAuthAdminService
and the application is not getting created in the IS database.
The code which I have tried goes as follows
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ConfigurationContextFactory;
import org.apache.axis2.transport.http.HTTPConstants;
import org.wso2.carbon.authenticator.proxy.AuthenticationAdminStub;
import org.wso2.carbon.identity.oauth.OAuthAdminServicePortTypeProxy;
import org.wso2.carbon.identity.oauth.dto.xsd.OAuthConsumerAppDTO;
public class IdentityClientOne {
private final static String SERVER_URL = "https://localhost:9443/services/";
private final static String APP_ID = "myapp";
/**
* #param args
*/
public static void main(String[] args) {
AuthenticationAdminStub authstub = null;
ConfigurationContext configContext = null;
System.setProperty("javax.net.ssl.trustStore", "wso2carbon.jks");
System.setProperty("javax.net.ssl.trustStorePassword", "wso2carbon");
try {
configContext = ConfigurationContextFactory.createConfigurationContextFromFileSystem(
"repo", "repo/conf/client.axis2.xml");
authstub = new AuthenticationAdminStub(configContext, SERVER_URL
+ "AuthenticationAdmin");
// Authenticates as a user having rights to add users.
if (authstub.login("admin", "admin", APP_ID)) {
System.out.println("admin authenticated");
OAuthConsumerAppDTO consumerApp = new OAuthConsumerAppDTO("Oauth-2.0",
"sample_app",
"",
"authorization_code implicit password client_credentials refresh_token urn:ietf:params:oauth:grant-type:saml2-bearer iwa:ntlm","","","");
OAuthAdminServicePortTypeProxy OAuthAdminProxy = new OAuthAdminServicePortTypeProxy();
OAuthAdminProxy.registerOAuthApplicationData(consumerApp);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Please help what should be done right ?
You have to access the stub via the authenticated session.
Could you try below.
public class Test {
private final static String SERVER_URL = "https://localhost:9443/services/";
public static void main(String[] args) throws RemoteException, OAuthAdminServiceException {
OAuthAdminServiceStub stub = new OAuthAdminServiceStub(null, SERVER_URL + "OAuthAdminService");
ServiceClient client = stub._getServiceClient();
authenticate(client);
OAuthConsumerAppDTO consumerAppDTO = new OAuthConsumerAppDTO();
consumerAppDTO.setApplicationName("sample-app");
consumerAppDTO.setCallbackUrl("http://localhost:8080/playground2/oauth2client");
consumerAppDTO.setOAuthVersion("OAuth-2.0");
consumerAppDTO.setGrantTypes("authorization_code implicit password client_credentials refresh_token "
+ "urn:ietf:params:oauth:grant-type:saml2-bearer iwa:ntlm");
stub.registerOAuthApplicationData(consumerAppDTO);
}
public static void authenticate(ServiceClient client) {
Options option = client.getOptions();
HttpTransportProperties.Authenticator auth = new HttpTransportProperties.Authenticator();
auth.setUsername("admin");
auth.setPassword("admin");
auth.setPreemptiveAuthentication(true);
option.setProperty(org.apache.axis2.transport.http.HTTPConstants.AUTHENTICATE, auth);
option.setManageSession(true);
}
}

CognitoCachingCredentialsProvider (Android) - how to logout and remove cached credentials

I'm using the developer-authenticated technique for implementing this class, as described here. So far, I've been able to implement this class and build a framework in which I check CognitoCachingCredentialsProvider.getCachedIdentityId() to see if a user has logged in (and therefore doesn't need to re-authenticate by entering an email and password). To do this, I'm using a series of static methods in a class called Util, since these only need to be instantiated once. This is what it looks like:
package com.pranskee.boxesapp;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.util.Log;
import com.amazonaws.auth.AWSAbstractCognitoIdentityProvider;
import com.amazonaws.auth.CognitoCachingCredentialsProvider;
import com.amazonaws.mobileconnectors.cognito.*;
import com.amazonaws.regions.Regions;
public class Util {
private final static String TAG = "Util";
private static final String AWS_ACCOUNT_ID = {acct id};
private static final String COGNITO_POOL_ID = {pool id};
private static final String COGNITO_ROLE_AUTH = {auth arn};
private static final String COGNITO_ROLE_UNAUTH = {unauth arn}
private static CognitoCachingCredentialsProvider sCredProvider;
private static UserIdentityProvider sIdProvider;
private static CognitoSyncManager sSyncManager;
private Util() {
}
public static CognitoCachingCredentialsProvider getCredProvider(
Context context) {
if (sCredProvider == null) {
if (sIdProvider == null) {
CognitoCachingCredentialsProvider tmpProvider = new CognitoCachingCredentialsProvider(
context.getApplicationContext(), AWS_ACCOUNT_ID,
COGNITO_POOL_ID, COGNITO_ROLE_UNAUTH,
COGNITO_ROLE_AUTH, Regions.US_EAST_1);
if (tmpProvider.getCachedIdentityId() != null) {
sCredProvider = tmpProvider;
} else {
sCredProvider = null;
}
} else {
sCredProvider = new CognitoCachingCredentialsProvider(
context.getApplicationContext(), sIdProvider,
COGNITO_ROLE_UNAUTH, COGNITO_ROLE_AUTH);
}
}
return sCredProvider;
}
public static UserIdentityProvider getIdentityProvider(Context context,
String email, String pwd) {
if (sIdProvider == null) {
sIdProvider = new UserIdentityProvider(AWS_ACCOUNT_ID,
COGNITO_POOL_ID, context.getApplicationContext(), email,
pwd);
Map logins = new HashMap();
logins.put({Developer Provider Name}, sIdProvider.getToken());
sIdProvider.setLogins(logins);
}
return sIdProvider;
}
public static boolean isLoggedIn(Context context) {
if (getCredProvider(context) == null) {
return false;
}
return true;
}
private static CognitoSyncManager getSyncManager(Context context) {
if (sSyncManager == null) {
sSyncManager = new CognitoSyncManager(
context.getApplicationContext(), Regions.US_EAST_1,
sCredProvider);
}
return sSyncManager;
}
protected static class UserIdentityProvider extends
AWSAbstractCognitoIdentityProvider {
private Context context;
private String email;
private String password;
public UserIdentityProvider(String accountId, String identityPoolId,
Context c, String em, String pwd) {
super(accountId, identityPoolId);
context = c;
email = em;
password = pwd;
}
#Override
public String refresh() {
try {
ServerCommunicator server = new ServerCommunicator(context);
//this is a server call, which makes the call GetOpenIdTokenForDeveloperIdentityRequest after I authenticate the user and send AWS my user's token
String response = server.initUserLoginAsyncTask()
.execute(email, password).get();
JSONObject responseJSON = new JSONObject(response);
String identityId = responseJSON.getString("id");
String token = responseJSON.getString("token");
this.setToken(token);
this.setIdentityId(identityId);
update(identityId, token);
return token;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
#Override
public String getProviderName() {
return {Developer Provider Name};
}
}
}
Now, I want to also implement a Logout. I think what I'd need to do is remove the cached Identity Id somehow, but I'm not sure what the best method would be to do that. Or, maybe it's not that at all, and I need to do something differently entirely. Either way, I just want to implement the intended behavior of allowing a user to select to "Log Out" of my app, which causes Cognito to forget that that ID was logged into the Identity Pool and invalidates any attempt to establish an Identity ID again without going through my authentication process again.
Logout would be a two steps process, first you need to logout from the Identity Provider that authenticated your user (Amazon, Google, Facebook or your own) Instructions on how to do this are specific to your provider.
From the CognitoIdentity side, you need to tell the CredentialsProvider to clear all state and cache associated with your identity. Using Android SDK, you can call clear() on the CredentialsProvider (see http://docs.aws.amazon.com/AWSAndroidSDK/latest/javadoc/com/amazonaws/auth/CognitoCredentialsProvider.html)

CognitoCachingCredentialsProvider getCachedIdentityId is null after app close and re-open

I might be misunderstanding the intended behavior of this method, but this is what I am trying to use it for:
-User logs in successfully
-User closes app completely (closes in background as well)
-User opens app again and doesn't have to log in again because CognitoCachingCredentialsProvider can check locally on the device to see she's still logged in
The way I tried to accomplish this is to check, before being prompted to log in, what getCachedIdentityId() returns. If it returns not null, then that means that she's still logged in, because there was nothing that cleared her credentials from the device. Here's what my framework looks like. I'm using developer authenticated method:
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.util.Log;
import com.amazonaws.auth.AWSAbstractCognitoIdentityProvider;
import com.amazonaws.auth.CognitoCachingCredentialsProvider;
import com.amazonaws.mobileconnectors.cognito.*;
import com.amazonaws.regions.Regions;
public class Util {
private final static String TAG = "Util";
private static final String AWS_ACCOUNT_ID = {acct id};
private static final String COGNITO_POOL_ID = {pool id};
private static final String COGNITO_ROLE_AUTH = {auth arn};
private static final String COGNITO_ROLE_UNAUTH = {unauth arn}
private static CognitoCachingCredentialsProvider sCredProvider;
private static UserIdentityProvider sIdProvider;
private static CognitoSyncManager sSyncManager;
private Util() {
}
public static CognitoCachingCredentialsProvider getCredProvider(
Context context) {
if (sCredProvider == null) {
if (sIdProvider == null) {
CognitoCachingCredentialsProvider tmpProvider = new CognitoCachingCredentialsProvider(
context.getApplicationContext(), AWS_ACCOUNT_ID,
COGNITO_POOL_ID, COGNITO_ROLE_UNAUTH,
COGNITO_ROLE_AUTH, Regions.US_EAST_1);
if (tmpProvider.getCachedIdentityId() != null) {
sCredProvider = tmpProvider;
} else {
sCredProvider = null;
}
} else {
sCredProvider = new CognitoCachingCredentialsProvider(
context.getApplicationContext(), sIdProvider,
COGNITO_ROLE_UNAUTH, COGNITO_ROLE_AUTH);
Map logins = new HashMap();
logins.put({Developer Provider Name}, sIdProvider.getToken());
sCredProvider.setLogins(logins);
}
}
return sCredProvider;
}
public static UserIdentityProvider getIdentityProvider(Context context,
String email, String pwd) {
if (sIdProvider == null) {
sIdProvider = new UserIdentityProvider(AWS_ACCOUNT_ID,
COGNITO_POOL_ID, context.getApplicationContext());
}
return sIdProvider;
}
public static boolean isLoggedIn(Context context) {
if (getCredProvider(context) == null) {
return false;
}
return true;
}
protected static class UserIdentityProvider extends
AWSAbstractCognitoIdentityProvider {
private Context context;
private String email;
private String password;
public UserIdentityProvider(String accountId, String identityPoolId,
Context c) {
super(accountId, identityPoolId);
context = c;
email = em;
password = pwd;
}
#Override
public String refresh() {
try {
ServerCommunicator server = new ServerCommunicator(context);
if (email != null && password != null) {
//this is a server call, which makes the call GetOpenIdTokenForDeveloperIdentityRequest after I authenticate the user and send AWS my user's token
String response = server.initUserLoginAsyncTask()
.execute(email, password).get();
prefs.setAllUserSharedPrefs(response);
JSONObject responseJSON = new JSONObject(response);
String identityId = responseJSON.getString("id");
String token = responseJSON.getString("token");
if (token != null && identityId != null) {
this.setToken(token);
this.setIdentityId(identityId);
update(identityId, token);
return token;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
#Override
public String getProviderName() {
return {Developer Provider Name};
}
}
}
I simply call the isLoggedIn method from this class to see if there's an IdentityId stored locally. However, this isn't working as expected. I can see from debugging that getCachedIdentityId is always null (even directly after initializing CognitoCachingCredentialsProvider and adding the token to the logins map) and I am always prompted to log in again whenever I open the app after it has been closed. When does the IdentityId actually get stored locally and is my logic correct in general?
Additional Code
import java.util.concurrent.ExecutionException;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class LoginActivity extends Activity {
private final String TAG = "LoginActivity";
private EditText etEmail, etPwd;
private Button bLogin, bGoToRegister;
private ServerCommunicator server;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate");
server = new ServerCommunicator(this);
if (Util.isLoggedIn(this)) {
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
return;
}
this.setContentView(R.layout.activity_login);
etEmail = (EditText) findViewById(R.id.etEmail);
etPwd = (EditText) findViewById(R.id.etPassword);
bLogin = (Button) findViewById(R.id.bLogin);
bGoToRegister = (Button) findViewById(R.id.bGoToRegister);
bLogin.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
String email = etEmail.getText().toString();
String pwd = etPwd.getText().toString();
Util.getIdentityProvider(v.getContext()).setEmail(email);
Util.getIdentityProvider(v.getContext()).setPassword(pwd);
String token = Util.getIdentityProvider(v.getContext()).refresh();
if (token != null) {
Intent intent = new Intent(v.getContext(), MainActivity.class);
startActivity(intent);
} else {
Toast.makeText(v.getContext(), "Invalid username/password",
Toast.LENGTH_SHORT).show();
}
}
});
}
}
The above is my LoginActivity. When the app starts the MainActivity, I have the following snippet at the beginning of my onCreate method:
if (!Util.isLoggedIn(this)) {
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
}
This call initializes the CognitoCachingCredentialsProvider. I assumed that this would be when the IdentityId gets cached, but my debugging has revealed that even directly after this block, getCachedIdentityId() still returns null. Am I way off base with how I'm trying to use this class?
I have one suggestion. The CognitoCachingCredentialsProvider is what saves the identityId as it's changed. It doesn't start listening, though, until it's been initialized, and the change occurs on refresh call to your identity provider.
Can you try moving the initialization of the CognitoCachingCredentialsProvider to before the refresh call (but after your identity provider initialization)?
Edit:
Update will set the identityId and token, however explicit calls made just beforehand may cause it to think no change is being made. Can you try eliminating the setter calls as well?