Skip to content

Commit

Permalink
[MobileClient] Add developer authenticated identities to federatedSig…
Browse files Browse the repository at this point in the history
…nIn fixes #577
  • Loading branch information
minbi committed Mar 6, 2019
1 parent 230c451 commit b0a07f6
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,13 @@
import android.util.Log;

import com.amazonaws.AmazonClientException;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSAbstractCognitoIdentityProvider;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSEnhancedCognitoIdentityProvider;
import com.amazonaws.auth.AWSSessionCredentials;
import com.amazonaws.auth.AnonymousAWSCredentials;
import com.amazonaws.auth.CognitoCachingCredentialsProvider;
import com.amazonaws.mobile.auth.core.IdentityManager;
import com.amazonaws.mobile.auth.core.SignInStateChangeListener;
Expand Down Expand Up @@ -76,7 +80,12 @@
import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.SignUpHandler;
import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.UpdateAttributesHandler;
import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.VerificationHandler;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.cognitoidentity.AmazonCognitoIdentity;
import com.amazonaws.services.cognitoidentity.AmazonCognitoIdentityClient;
import com.amazonaws.services.cognitoidentity.model.NotAuthorizedException;
import com.amazonaws.util.StringUtils;

import org.json.JSONObject;

Expand Down Expand Up @@ -208,6 +217,7 @@ public final class AWSMobileClient implements AWSCredentialsProvider {
private Object federateWithCognitoIdentityLockObject;
private Object initLockObject;
AWSMobileClientStore mStore;
AWSMobileClientCognitoIdentityProvider provider;

/**
* Constructor invoked by getInstance.
Expand Down Expand Up @@ -395,9 +405,19 @@ public void onUserSignedOut() {
});

if (awsConfiguration.optJsonObject("CredentialsProvider") != null
&& awsConfig.optJsonObject("CredentialsProvider").optJSONObject("CognitoIdentity") != null) {
&& awsConfiguration.optJsonObject("CredentialsProvider").optJSONObject("CognitoIdentity") != null) {
try {
cognitoIdentity = new CognitoCachingCredentialsProvider(mContext, awsConfiguration);
JSONObject identityPoolJSON = awsConfiguration.optJsonObject(
"CredentialsProvider").getJSONObject("CognitoIdentity").getJSONObject(awsConfiguration.getConfiguration());
final String poolId = identityPoolJSON.getString("PoolId");
final String regionStr = identityPoolJSON.getString("Region");
AmazonCognitoIdentityClient cibClient =
new AmazonCognitoIdentityClient(new AnonymousAWSCredentials());
cibClient.setRegion(Region.getRegion(regionStr));
provider = new AWSMobileClientCognitoIdentityProvider(
null, poolId, cibClient);
cognitoIdentity = new CognitoCachingCredentialsProvider(
mContext, provider, Regions.fromName(regionStr));
} catch (Exception e) {
callback.onError(new RuntimeException("Failed to initialize Cognito Identity; please check your awsconfiguration.json", e));
return;
Expand Down Expand Up @@ -856,7 +876,7 @@ public void federatedSignIn(final String providerKey,
final String token,
final Callback<UserStateDetails> callback) {
InternalCallback<UserStateDetails> internalCallback = new InternalCallback<UserStateDetails>(callback);
internalCallback.async(_federatedSignIn(providerKey, token, internalCallback, true));
internalCallback.async(_federatedSignIn(providerKey, token, null, internalCallback, true));
}

/**
Expand All @@ -868,25 +888,59 @@ public void federatedSignIn(final String providerKey,
* @param providerKey Custom provider key i.e. Google sign-in's key is accounts.google.com
* @param token the JWT token vended by the third-party
*/
public void federatedSignIn(final String providerKey, final String token) throws Exception {
public UserStateDetails federatedSignIn(final String providerKey, final String token) throws Exception {
InternalCallback<UserStateDetails> internalCallback = new InternalCallback<UserStateDetails>();
internalCallback.await(_federatedSignIn(providerKey, token, internalCallback, true));
return internalCallback.await(_federatedSignIn(providerKey, token, null, internalCallback, true));
}

/**
* Federate tokens from custom identity providers by providing the
* logins key and token
* <p>
* The logins key can be specified with {@link IdentityProvider#AMAZON#toString()}
*
* @param providerKey Custom provider key i.e. Google sign-in's key is accounts.google.com
* @param token the JWT token vended by the third-party
*/
public void federatedSignIn(final String providerKey,
final String token,
final FederatedSignInOptions options,
final Callback<UserStateDetails> callback) {
InternalCallback<UserStateDetails> internalCallback = new InternalCallback<UserStateDetails>(callback);
internalCallback.async(_federatedSignIn(providerKey, token, options, internalCallback, true));
}

/**
* Federate tokens from custom identity providers by providing the
* logins key and token
* <p>
* The logins key can be specified with {@link IdentityProvider#AMAZON}
*
* @param providerKey Custom provider key i.e. Google sign-in's key is accounts.google.com
* @param token the JWT token vended by the third-party
*/
public UserStateDetails federatedSignIn(final String providerKey,
final String token,
final FederatedSignInOptions options) throws Exception {
InternalCallback<UserStateDetails> internalCallback = new InternalCallback<UserStateDetails>();
return internalCallback.await(_federatedSignIn(providerKey, token, options, internalCallback, true));
}

protected void federatedSignInWithoutAssigningState(final String providerKey, final String token) throws Exception {
InternalCallback<UserStateDetails> internalCallback = new InternalCallback<UserStateDetails>();
internalCallback.await(_federatedSignIn(providerKey, token, internalCallback, false));
internalCallback.await(_federatedSignIn(providerKey, token, null, internalCallback, false));
}

protected void federatedSignInWithoutAssigningState(final String providerKey,
final String token,
final Callback<UserStateDetails> callback) {
InternalCallback<UserStateDetails> internalCallback = new InternalCallback<UserStateDetails>(callback);
internalCallback.async(_federatedSignIn(providerKey, token, internalCallback, false));
internalCallback.async(_federatedSignIn(providerKey, token, null, internalCallback, false));
}

private Runnable _federatedSignIn(final String providerKey,
final String token,
final FederatedSignInOptions options,
final Callback<UserStateDetails> callback,
final boolean assignState) {

Expand All @@ -913,6 +967,17 @@ public void run() {
cognitoIdentity.clear();
cognitoIdentity.setLogins(loginsMap);
}

if (IdentityProvider.DEVELOPER.equals(providerKey)) {
provider.setDeveloperAuthenticated(options.getIdentityId(), token);
} else {
provider.setNotDeveloperAuthenticated();
}

if (!StringUtils.isBlank(options.getCustomRoleARN())) {
cognitoIdentity.setCustomRoleArn(options.getCustomRoleARN());
}

UserStateDetails userStateDetails = getUserStateDetails(true);

new Thread(new Runnable() {
Expand Down Expand Up @@ -2509,3 +2574,96 @@ void clear() {
mSharedPreferences.edit().clear().commit();
}
}

/**
* A duplicate class of AWSEnhancedCognitoIdentityProvider that provides the ability to
* branch into developer authenticated identities.
*/
class AWSMobileClientCognitoIdentityProvider extends AWSAbstractCognitoIdentityProvider {

boolean isDeveloperAuthenticated;

/**
* An extension of the AbstractCognitoProvider that is used to communicate
* with Cognito.
*
* @param accountId the account id of the developer
* @param identityPoolId the identity pool id of the app/user in question
*/
public AWSMobileClientCognitoIdentityProvider(String accountId, String identityPoolId) {
this(accountId, identityPoolId, new ClientConfiguration());
}

/**
* An extension of the AbstractCognitoProvider that is used to communicate
* with Cognito.
*
* @param accountId the account id of the developer
* @param identityPoolId the identity pool id of the app/user in question
* @param clientConfiguration the configuration to apply to service clients
* created
*/
public AWSMobileClientCognitoIdentityProvider(String accountId, String identityPoolId,
ClientConfiguration clientConfiguration) {
this(accountId, identityPoolId, new AmazonCognitoIdentityClient
(new AnonymousAWSCredentials(), clientConfiguration));
}

/**
* An extension of the AbstractCognitoProvider that is used to communicate
* with Cognito.
*
* @param accountId the account id of the developer
* @param identityPoolId the identity pool id of the app/user in question
* @param cibClient the cib client which will be used to contact the cib
* back end
*/
public AWSMobileClientCognitoIdentityProvider(String accountId, String identityPoolId,
AmazonCognitoIdentity cibClient) {
super(accountId, identityPoolId, cibClient);
}

@Override
protected String getUserAgent() {
return "AWSMobileClient";
}

/**
* Internal method that switches the flow of the {@link com.amazonaws.auth.CognitoCredentialsProvider}
* to be the developer authenticated flow.
*
* @param identityId provided by user upstream
* @param token provided by user upstream
*/
void setDeveloperAuthenticated(final String identityId,
final String token) {
super.setIdentityId(identityId);
super.setToken(token);
isDeveloperAuthenticated = true;
}

/**
* Internal method that switches the flow of the {@link com.amazonaws.auth.CognitoCredentialsProvider}
* to be the Cognito authenticated flow.
*/
void setNotDeveloperAuthenticated() {
isDeveloperAuthenticated = false;
}

@Override
public String getProviderName() {
return "Cognito";
}

@Override
public String refresh() {
if (isDeveloperAuthenticated) {
// The identity id is already set in the setDeveloperAuthenticated call
return this.token;
} else {
getIdentityId();
return null;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.amazonaws.mobile.client;

public class FederatedSignInOptions {

private final Builder builder;

FederatedSignInOptions(final Builder builder) {
this.builder = builder;
}

public String getCustomRoleARN() {
return builder.customRoleARN;
}

public String getIdentityId() {
return builder.identityId;
}

/**
* Start creating a SignOutOptions object with this builder.
*
* @return a Builder used to specify options
*/
public static Builder builder() {
return new Builder();
}

public static class Builder {
private String customRoleARN;
private String identityId;

public Builder() { }

public Builder identityId(final String identityId) {
this.identityId = identityId;
return this;
}

public Builder customRoleARN(final String customRoleARN) {
this.customRoleARN = customRoleARN;
return this;
}

/**
* Finalize the Builder and the options object will be returned.
*
* @return the options object containing the options specified
*/
public FederatedSignInOptions build() {
return new FederatedSignInOptions(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ public enum IdentityProvider {
AMAZON("www.amazon.com"),
FACEBOOK("graph.facebook.com"),
GOOGLE("accounts.google.com"),
TWITTER("api.twitter.com");
TWITTER("api.twitter.com"),
DEVELOPER("cognito-identity.amazonaws.com"),
;

private final String key;

Expand All @@ -35,4 +37,13 @@ public enum IdentityProvider {
public String toString() {
return this.key;
}

/**
* Utility comparison for the String value of the enum
* @param other The String that is being compared to the IdentityProvider's string value
* @return
*/
public boolean equals(final String other) {
return key.equals(other);
}
}

0 comments on commit b0a07f6

Please sign in to comment.