Android Amazon SNS Invalid Parameter - android

Code Returns Exception Invalid Paramters
I need to know what are the invalid params ?
Is Application ARN string value from where to get it or it is a static value .
when to send device if returned from GCM ?
CreatePlatformEndpointRequest cpeReq =
new CreatePlatformEndpointRequest();
cpeReq.setCustomUserData("");
cpeReq.setToken(this.mToken);
cpeReq.setPlatformApplicationArn(retrieveEndpointArn());
client.setRegion(Region.getRegion(Regions.DEFAULT_REGION));
CreatePlatformEndpointResult cpeRes = client
.createPlatformEndpoint(cpeReq);
endpointArn = cpeRes.getEndpointArn();
} catch (InvalidParameterException ipe) {
String message = ipe.getErrorMessage();
System.out.println("Exception message: " + message);
Pattern p = Pattern
.compile(".*Endpoint (arn:aws:sns[^ ]+) already exists " +
"with the same Token.*");
Matcher m = p.matcher(message);
if (m.matches()) {
// the endpoint already exists for this token, but with
// additional custom data that
// CreateEndpoint doesn't want to overwrite. Just use the
// existing endpoint.
endpointArn = m.group(1);
} else {
// rethrow exception, the input is actually bad
throw ipe;
}
}

Related

InvalidConfigurationException: Identity pool isn't set up for SNS using AmazonCognitoSync service

I have been trying to use AWS SDKs for Push Notifications. But I am getting errors. Tried to find a solution, but can't find much support for this.
iOS & Web push notifications are working fine
What all is already setup & done:
AWS back-end & console setting in place.
Identity Pool Id & other keys in place.
ARN topic in place.
Android side:
AWS SDK dependencies:
implementation 'com.amazonaws:aws-android-sdk-core:2.16.8'
implementation 'com.amazonaws:aws-android-sdk-cognito:2.6.23'
implementation 'com.amazonaws:aws-android-sdk-s3:2.15.1'
implementation 'com.amazonaws:aws-android-sdk-ddb:2.2.0'
implementation ('com.amazonaws:aws-android-sdk-mobile-client:2.16.8') { transitive = true; }
minSdkVersion 21
targetSdkVersion 29
Inside onCreate:
CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
getApplicationContext(),
"My Pool Id here", // Identity pool ID
Regions.US_EAST_1 // Region
);
CognitoSyncManager client = new CognitoSyncManager(
LoginActivateActivity.this,
Regions.US_EAST_1,
credentialsProvider);
String registrationId = "MY_FCM_DEVICE_TOKEN"; **Instead of GCM ID, I am passing my unique FCM device token here. I searched, & it seems that wherever GCM is required, it is being replaced by FCM.**
try {
client.registerDevice("GCM", registrationId);
} catch (RegistrationFailedException rfe) {
Log.e("TAG", "Failed to register device for silent sync", rfe);
} catch (AmazonClientException ace) {
Log.e("TAG", "An unknown error caused registration for silent sync to fail", ace);
}
Dataset trackedDataset = client.openOrCreateDataset("My Topic here");
if (client.isDeviceRegistered()) {
try {
trackedDataset.subscribe();
} catch (SubscribeFailedException sfe) {
Log.e("TAG", "Failed to subscribe to datasets", sfe);
} catch (AmazonClientException ace) {
Log.e("TAG", "An unknown error caused the subscription to fail", ace);
}
}
Error I am getting on client.registerDevice("GCM", registrationId);
Caused by: com.amazonaws.services.cognitosync.model.InvalidConfigurationException: Identity pool isn't set up for SNS (Service: AmazonCognitoSync; Status Code: 400; Error Code: InvalidConfigurationException; Request ID: a858aaa2-**************************)
Note:
I tried using Amplify libraries, but even that didn't work. Also, at iOS & Web end they are using AWS SDK. So I am also bound to use the same. This is not even a device specific error.
All I need to do is setup my project to get push notifications. But I am stuck at the initial step. Not able to create an endpoint for Android device.
I actually found the solution to the issue, thanks to a friend who shared this link:
https://aws.amazon.com/premiumsupport/knowledge-center/create-android-push-messaging-sns/
This Youtube video also helped a lot:
https://www.youtube.com/watch?v=9QSO3ghSUNk&list=WL&index=3
Edited code
private void registerWithSNS() {
CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
getApplicationContext(),
"Your Identity Pool ID",
Regions.US_EAST_1 // Region
);
client = new AmazonSNSClient(credentialsProvider);
String endpointArn = retrieveEndpointArn();
String token = "Your FCM Registration ID generated for the device";
boolean updateNeeded = false;
boolean createNeeded = (null == endpointArn || "".equalsIgnoreCase(endpointArn));
if (createNeeded) {
// No platform endpoint ARN is stored; need to call createEndpoint.
endpointArn = createEndpoint(token);
createNeeded = false;
}
System.out.println("Retrieving platform endpoint data...");
// Look up the platform endpoint and make sure the data in it is current, even if
// it was just created.
try {
GetEndpointAttributesRequest geaReq =
new GetEndpointAttributesRequest()
.withEndpointArn(endpointArn);
GetEndpointAttributesResult geaRes =
client.getEndpointAttributes(geaReq);
updateNeeded = !geaRes.getAttributes().get("Token").equals(token)
|| !geaRes.getAttributes().get("Enabled").equalsIgnoreCase("true");
} catch (NotFoundException nfe) {
// We had a stored ARN, but the platform endpoint associated with it
// disappeared. Recreate it.
createNeeded = true;
} catch (AmazonClientException e) {
createNeeded = true;
}
if (createNeeded) {
createEndpoint(token);
}
System.out.println("updateNeeded = " + updateNeeded);
if (updateNeeded) {
// The platform endpoint is out of sync with the current data;
// update the token and enable it.
System.out.println("Updating platform endpoint " + endpointArn);
Map attribs = new HashMap();
attribs.put("Token", token);
attribs.put("Enabled", "true");
SetEndpointAttributesRequest saeReq =
new SetEndpointAttributesRequest()
.withEndpointArn(endpointArn)
.withAttributes(attribs);
client.setEndpointAttributes(saeReq);
}
}
/**
* #return never null
* */
private String createEndpoint(String token) {
String endpointArn = null;
try {
System.out.println("Creating platform endpoint with token " + token);
CreatePlatformEndpointRequest cpeReq =
new CreatePlatformEndpointRequest()
.withPlatformApplicationArn("Your Platform ARN. This you get from AWS Console. Unique for all devices for a platform.")
.withToken(token);
CreatePlatformEndpointResult cpeRes = client
.createPlatformEndpoint(cpeReq);
endpointArn = cpeRes.getEndpointArn();
} catch (InvalidParameterException ipe) {
String message = ipe.getErrorMessage();
System.out.println("Exception message: " + message);
Pattern p = Pattern
.compile(".*Endpoint (arn:aws:sns[^ ]+) already exists " +
"with the same [Tt]oken.*");
Matcher m = p.matcher(message);
if (m.matches()) {
// The platform endpoint already exists for this token, but with
// additional custom data that
// createEndpoint doesn't want to overwrite. Just use the
// existing platform endpoint.
endpointArn = m.group(1);
} else {
// Rethrow the exception, the input is actually bad.
throw ipe;
}
}
storeEndpointArn(endpointArn);
return endpointArn;
}
/**
* #return the ARN the app was registered under previously, or null if no
* platform endpoint ARN is stored.
*/
private String retrieveEndpointArn() {
// Retrieve the platform endpoint ARN from permanent storage,
// or return null if null is stored.
return endpointArn;
}
/**
* Stores the platform endpoint ARN in permanent storage for lookup next time.
* */
private void storeEndpointArn(String endpointArn) {
// Write the platform endpoint ARN to permanent storage.
UserSession.getSession(LoginActivateActivity.this).setARN(endpointArn); //Your platform endpoint ARN. This is unique for each device, but changes when
}
Once an endpoint is created for the device, you need to store the endpointArn & FCM registration ID to your DB on server-side. Rest of the code will be your FCM implementation code for receiving notifications.
Hope this helps someone

How to decode SafetyNet JWS response?

I am investigating SafetyNet provided by Google within my Android Application.
To start with I simply called the SafetyNet attest API and Base64 decoded the parts as shown in the Google supplied examples.
SafetyNet.getClient(this).attest(NONCE, <API KEY>)
.addOnSuccessListener(this, new OnSuccessListener<SafetyNetApi.AttestationResponse>() {
#Override
public void onSuccess(final SafetyNetApi.AttestationResponse attestationResponse) {
initialDataExtraction(attestationResponse.getJwsResult());
}
})
.addOnFailureListener(this, new OnFailureListener() {
#Override
public void onFailure(#NonNull final Exception exception) {
if (exception instanceof ApiException) {
final ApiException apiException = (ApiException) exception;
Log.e(TAG, "onFailure: " + apiException.getMessage() + " " + apiException.getStatusCode());
} else {
Log.e(TAG, "Error: ", exception);
}
}
});
I extract the JWS parts as follows:-
private byte[] initialDataExtraction(final String jwsResult) {
final String[] jwsResultParts = jwsResult.split("[.]");
if (jwsResultParts.length == 3) {
final byte[] header = Base64.decode(jwsResultParts[0], Base64.NO_WRAP);
final byte[] data = Base64.decode(jwsResultParts[1], Base64.NO_WRAP);
final byte[] signature = Base64.decode(jwsResultParts[2], Base64.NO_WRAP);
Log.d(TAG, "initialDataExtraction: header = " + new String(header, UTF_8));
Log.d(TAG, "initialDataExtraction: data = " + new String(data, UTF_8));
Log.d(TAG, "initialDataExtraction: signature = " + new String(signature, UTF_8));
return data;
} else {
Log.e(TAG, "initialDataExtraction: Failure: Illegal JWS signature format. The JWS consists of " + jwsResultParts.length + " parts instead of 3.");
return null;
}
}
I am using android.util.Base64 to decode the parts and the majority of the time the decoding completes OK.
Occasionally I receive this exception though:-
java.lang.IllegalArgumentException: bad base-64
at android.util.Base64.decode(Base64.java:161)
at android.util.Base64.decode(Base64.java:136)
at android.util.Base64.decode(Base64.java:118)
when decoding the Signature part.
What am I doing wrong when decoding to see this intermittent error?
I then moved onto to using a JWT library to decode the tokens.
first I tried group: 'com.auth0.android', name: 'jwtdecode', version: '1.1.1'
the code I tried is
final JWT jwt = new JWT(jwsResult);
which consistently fails with the following error
com.auth0.android.jwt.DecodeException: The token's payload had an invalid JSON format.
at com.auth0.android.jwt.JWT.parseJson(JWT.java:235)
at com.auth0.android.jwt.JWT.decode(JWT.java:203)
at com.auth0.android.jwt.JWT.<init>(JWT.java:40)
Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_ARRAY at line 1 column 23 path $.
at com.google.gson.Gson.fromJson(Gson.java:899)
at com.google.gson.Gson.fromJson(Gson.java:852)
at com.google.gson.Gson.fromJson(Gson.java:801)
This exception seems to be caused by the Auth0 library being unable to parse headers 4.1.6. "x5c" (X.509 Certificate Chain) Header format which is odd as the JWS Spec clearly states the value is represented by a JSON aray:-
The "x5c" (X.509 Certificate Chain) Header Parameter contains the
X.509 public key certificate or certificate chain [RFC5280]
corresponding to the key used to digitally sign the JWS. The
certificate or certificate chain is represented as a JSON array of
certificate value strings.
However If I copy and paste the same jws result string into a pure java project and use compile 'com.auth0:java-jwt:3.3.0' and use this code:-
String token = "<JWS TOKEN>";
try {
final DecodedJWT jwt = JWT.decode(token);
System.out.println("Header = " + jwt.getHeader());
System.out.println("Payload = " + jwt.getPayload());
System.out.println("Signature = " + jwt.getSignature());
} catch (JWTDecodeException exception){
throw new RuntimeException(exception);
}
The Jws Token is decoded successfully.
What am I doing wrong within my Android application that stops the auth0 android jwt library working as desired?
I then tried 'io.jsonwebtoken:jjwt:0.9.0' library within my Android application.
When I execute this code:-
Jwts.parser().parse(jwsResult).getBody();
it fails with:-
java.lang.IllegalArgumentException: A signing key must be specified if the specified JWT is digitally signed.
at io.jsonwebtoken.lang.Assert.notNull(Assert.java:85)
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:331)
What signing key do I need to pass to Jwts? The only key I have is my API key held in the Google API Console, is this the key I should employ?
when I pass it as follows:
Jwts.parser().setSigningKey<API KEY>.parse(jwsResult).getBody();
this fails with:-
java.lang.IllegalArgumentException: Key bytes can only be specified for HMAC signatures. Please specify a PublicKey or PrivateKey instance.
at io.jsonwebtoken.lang.Assert.isTrue(Assert.java:38)
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:324)
What is the correct approach to decode and consume the Jws result received from SafetyNet attest API call?
I discovered a fix for the java.lang.IllegalArgumentException: bad base-64 issue from this question Base64: java.lang.IllegalArgumentException: Illegal character
simply replace characters in jws token before decoding
token.replace('-', '+').replace('_', '/')
I identified this library not only does it do the job it works fine on Android.
// https://mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt
implementation group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: '5.1'
try {
final JWSObject jwsObject = JWSObject.parse(jwsResult);
System.out.println("header = " + jwsObject.getHeader());
System.out.println("header = " + jwsObject.getHeader().getX509CertChain());
System.out.println("payload = " + jwsObject.getPayload().toJSONObject());
System.out.println("signature = " + jwsObject.getSignature());
System.out.println("signature = " + jwsObject.getSignature().decodeToString());
} catch (ParseException e) {
e.printStackTrace();
}
Some nice examples are provided:-
https://connect2id.com/products/nimbus-jose-jwt/examples

Push notifications not received when called inside android app - Amazon SNS

I have a problem with my app based on AWS. When I test the following function in Amazon lambda, everything works (I get the push notification on my phone):
console.log("Loading kupa function");
var AWS = require("aws-sdk");
exports.handler = function(event, context) {
var eventText = JSON.stringify(event, null, 2);
console.log("Received event:", eventText);
var sns = new AWS.SNS();
var params = {
Message: eventText,
Subject: "Test SNS From Lambda",
TopicArn: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
};
sns.publish(params, context.done);
context.succeed("kupa sukces");
};
However, once I use the following method on my phone I get the "kupa sukces" log into my Android Studio but I don't get the notification on the phone. Furthermore, the "Test" on Lambda does not work anymore as wel...
Here is the code:
String lambdaRequest = "{\n\"kupa\" : \"" + true + "\"\n}";
asyncTask.delegate = wysylaczKupy.this;
asyncTask.friendFunction("friendsRequest",lambdaRequest);
}
the friendFunction is here:
public static void friendFunction(String funName, String requestContent) {
final String functionName = funName;
final String requestPayload = requestContent;
new AsyncTask<Void, Void, InvokeResult>() {
#Override
protected InvokeResult doInBackground(Void... params) {
try {
final ByteBuffer payload =
ENCODER.encode(CharBuffer.wrap(requestPayload));
final InvokeRequest invokeRequest =
new InvokeRequest()
.withFunctionName(functionName)
.withInvocationType(InvocationType.RequestResponse)
.withPayload(payload);
final InvokeResult invokeResult =
AWSMobileClient
.defaultMobileClient()
.getCloudFunctionClient()
.invoke(invokeRequest);
return invokeResult;
} catch (final Exception e) {
Log.e("LAMBDA", "AWS Lambda invocation failed : " + e.getMessage(), e);
final InvokeResult result = new InvokeResult();
result.setStatusCode(500);
result.setFunctionError(e.getMessage());
return result;
}
}
}
How can I fix this?
Thank you in advance,
Jan
Jan,
The Lambda function for publishing to an SNS Topic wasn't quite right. I modified your function and provided a default json value for testing. Just put your TopicARN in and try it out. Once you have tested using the Lambda console, then try the Android code, which I did not try.
Note that when sending a JSON payload to an SNS Topic, a default value is required. The default value is used when you don't specify a protocol specific message. For example, you are publishing to an SNS Topic with Android GCM endpoints and since your JSON payload does not contain "GCM" then all endpoints will receive your default message that you provided.
I'm not sure what you were doing with "{\n\"kupa\" : \"" + true + "\"\n}" but I'm guessing the "kupa": "true" is intended to the the data key/value for the app to handle? If so, you'll need to lookup a proper GCM payload to send both a message and data.
//Pass in the following json for testing: { "default":"some message", "kupa": "true"}
console.log("Loading kupa function");
var AWS = require("aws-sdk");
exports.handler = function(event, context, callback) {
var eventText = JSON.stringify(event, null, 2);
console.log("Received event:", eventText);
var sns = new AWS.SNS();
var params = {
Message: eventText,
MessageStructure: "json",
Subject: "Test SNS From Lambda",
TopicArn: "arn:aws:sns:us-west-2:xxxxxxxxxx:test"
};
sns.publish(params, function (err, data) {
if(err){
callback(err, null); //failed
}
callback(null, "kupa sukces"); //success
});
};

com.google.apphosting.api.ApiProxy$CancelledException: The API call urlfetch.Fetch() was explicitly cancelled. Code 503 App Engine Backend Error

I am using Google App Engine as backend for GCM. It is working fine except below error:
{ "error": { "errors": [ {
"domain": "global",
"reason": "backendError",
"message": "com.google.apphosting.api.ApiProxy$CancelledException: The API call urlfetch.Fetch() was explicitly cancelled." } ],
"code": 503, "message":
"com.google.apphosting.api.ApiProxy$CancelledException: The API call
urlfetch.Fetch() was explicitly cancelled." } }
To find cause I searched urlfetch.Fetch() method in my entire code, but I dint find it.
Does anyone got this same error before? If so then please tell me how you resolved it?
For more info, I added below code to send push notification for more than 1000 users.
private List<RegistrationRecord> regIdTrim(List<RegistrationRecord> wholeList, final int start) {
List<RegistrationRecord> parts = wholeList.subList(start,(start+1000)> wholeList.size()? wholeList.size() : start+1000);
return parts;
}
/**
* Send to the first 1000 devices (You can modify this to send to any number of devices or a specific device)
*
* #param message The message to send
*/
public void sendMessage(#Named("message") String message) throws IOException {
int count = ofy().load().type(RegistrationRecord.class).count();
if(count<=1) {
List<RegistrationRecord> records = ofy().load().type(RegistrationRecord.class).limit(count).list();
sendMsg(records,message);
}else
{
int msgsDone=0;
List<RegistrationRecord> records = ofy().load().type(RegistrationRecord.class).list();
do {
List<RegistrationRecord> regIdsParts = regIdTrim(records, msgsDone);
msgsDone+=1000;
sendMsg(regIdsParts,message);
}while(msgsDone<count);
}
}
private void sendMsg(List<RegistrationRecord> records,#Named("message") String msgToSend) throws IOException {
if (msgToSend == null || msgToSend.trim().length() == 0) {
log.warning("Not sending msgToSend because it is empty");
return;
}
Sender sender = new Sender(API_KEY);
// Message msg = new Message.Builder().addData("msgToSend", msgToSend).build();
Message msg = new Message.Builder().
addData("notification_title", msgToSend)
.addData("description","Check ").build();
// crop longer messages
if (msgToSend.length() > 1000) {
msgToSend = msgToSend.substring(0, 1000) + "[...]";
}
for (RegistrationRecord record : records) {
Result result = sender.send(msg, record.getRegId(), 5);
if (result.getMessageId() != null) {
log.info("Message sent to " + record.getRegId());
String canonicalRegId = result.getCanonicalRegistrationId();
if (canonicalRegId != null) {
// if the regId changed, we have to update the datastore
log.info("Registration Id changed for " + record.getRegId() + " updating to " + canonicalRegId);
record.setRegId(canonicalRegId);
ofy().save().entity(record).now();
}
} else {
String error = result.getErrorCodeName();
if (error.equals(Constants.ERROR_NOT_REGISTERED)) {
log.warning("Registration Id " + record.getRegId() + " no longer registered with GCM, removing from datastore");
// if the device is no longer registered with Gcm, remove it from the datastore
ofy().delete().entity(record).now();
} else {
log.warning("Error when sending msgToSend : " + error);
}
}
}
}
It looks like server is blocking you :/
I believe it has something to do with the 1000 network requests you're shooting at it.
Send the server a list of the 1000 ids you want to push to them, and let the server handle the pushes.

Android: Google Data API - 401 Token Invalid Error

Well, I tried using Account Manager and I am getting "401 Token Invalid" error. What can be the reason.
Debug Log
Account name = xxxxxx#gmail.com
Token is : DQAAALIAAAAh-xxxxxxx
Starting Google DATA API loader-----------------
Inside Google Notebook loader-----------------
Setting Token : DQAAALIAAAAh-xxxxx
Url is : https://docs.google.com/feeds/default/private/full
Exception getting docs feed : 401 Token invalid
Done Google DATA API loader-----------------
Sample Code:
Log.d("Main","\tInside GoogleDATA API -----------------");
HttpTransport transport = new NetHttpTransport();
GoogleHeaders headers = new GoogleHeaders();
Log.d("Main","\tSetting Token : " + authToken);
headers.setGoogleLogin(authToken);
headers.gdataVersion="3.0";
transport.defaultHeaders = headers;
AtomParser parser = new AtomParser();
parser.namespaceDictionary = Namespace.DICTIONARY;
transport.addParser(parser);
try {
DocsUrl url = DocsUrl.forDefaultPrivateFull();
DocumentListFeed feed = DocumentListFeed.executeGet(transport,url);
List<DocumentListEntry> docs = feed.docs;
Log.d("Main","\tDocs count = " + docs.size());
for (Iterator iterator = docs.iterator(); iterator.hasNext();) {
DocumentListEntry documentListEntry = (DocumentListEntry) iterator
.next();
Log.d("Main","\t\tDocument title is : " + documentListEntry.title);
}
} catch (IOException e) {
Log.d("Main","Exception getting docs feed : " + e.getMessage());
//handleException(e);
}
Log.d("Main","\tDone GoogleDATA API -----------------");
you should see how it works on this website http://n01se.net/gmapez/start.html

Categories

Resources