Empty json data in Release build (using proguard) - android

My app doesn't post data to server when I run the release version (signed, Proguard packed). But if I run it directly from ADT, I see the data on the server.
It's weird because it just the same code, one is signed and the other is direct execution. Here is the Code (using org.springframework.web.client.RestTemplate):
private static String URL = "https://myappserver.com/abcservice/";
public ResponseEntity<String> sendMessage(UserMessage us) {
private RestTemplate template = getTemplate();
HttpEntity<UserMessage> reqEntity = new HttpEntity<UserMessage>(us, headers);
ResponseEntity<String> result =
template.postForEntity(URL, reqEntity, String.class);
return result;
}
Below are the 2 scenarios:
Case 1: Works Good
Run the App directly from ADT (Run as: Android Application)
User taps a button, which will invoke sendMessage(..) method.
Controller (on Server) gets the request and the data (UserMessage).
An entry with UserMessage is created.
Server send a 201 response (request has been fulfilled), to the app.
Case 2: PROBLEM
Pack the app (Android Tools -> Export Signed Application Package..)
Install it on device through command line (adb install xxx.apk)
Start the app.
User taps a button, which will invoke sendMessage(..) method.
Controller (on Server) gets the request but no data.
An entry with empty UserMessage is created.
Server send a 201 response (request has been fulfilled), to the app.
I have tried to log both on my device and web server, I can confirm that empty data is received for Case 2, and I'm not able to figure out why it doesn't send data when I pack it?
Does packed/released (Signed +Proguard) apps behave differently, compared to debug packaging?

I guess that it is caused by proguard. Proguard is probably obfuscating some portion of your code, but this code is dynamically invoked by Spring (and jackson ? you didn't mention it). (and so once it is obfuscate : dynamic invocation failed)
So try to :
disable proguard to confirm that it is the cause of the problem
if it is confirmed : try to configure it so hat it won't obfuscate class that are serialized in json (i.e. UserMessage) :
-keep class com.company.UserMessage** { *; }

I have same issue, and I committed below 2 line
// shrinkResources true
// minifyEnabled true

The above answer didnt work for me may be it was correct on 2014
For 2019 when I set the compileSdkVersion 28 it works.

Related

Deleting a user from Azure Active Directory B2C Android/Java

I have an Android application in which I'm using Azure AD B2C to authenticate users. Users login and logout of the application as needed. I would like to give the user the option to delete their own account.
I understand that I need to use the Azure AD Graph API to delete the user. This is what I have so far:
According to this link, it looks like deleting a user from a personal account (which is what the B2C users are using) is not possible. Is that correct?
Here's my code snippet for the Graph API call. Feel free to ignore it if I'm off track and there is a better way to solve this.
I believe I need a separate access token than what my app currently has (as the graph API requires other API consent). So, I'm getting the access token as follows:
AcquireTokenParameters parameters = new AcquireTokenParameters.Builder()
.startAuthorizationFromActivity(getActivity())
.fromAuthority(B2CConfiguration.getAuthorityFromPolicyName(B2CConfiguration.Policies.get("SignUpSignIn")))
.withScopes(B2CConfiguration.getGraphAPIScopes())
.withPrompt(Prompt.CONSENT)
.withCallback(getGraphAPIAuthCallback())
.build();
taxApp.acquireToken(parameters);
In the getGraphAPIAuthCallback() method, I'm calling the Graph API using a separate thread (in the background):
boolean resp = new DeleteUser().execute(authenticationResult.getAccessToken()).get();
Finally, in my DeleterUser() AsyncTask, I'm doing the following:
#Override
protected Boolean doInBackground(String... aToken) {
final String asToken = aToken[0];
//this method will be running on background thread so don't update UI from here
//do your long running http tasks here,you dont want to pass argument and u can access the parent class' variable url over here
IAuthenticationProvider mAuthenticationProvider = new IAuthenticationProvider() {
#Override
public void authenticateRequest(final IHttpRequest request) {
request.addHeader("Authorization",
"Bearer " + asToken);
}
};
final IClientConfig mClientConfig = DefaultClientConfig
.createWithAuthenticationProvider(mAuthenticationProvider);
final IGraphServiceClient graphClient = new GraphServiceClient.Builder()
.fromConfig(mClientConfig)
.buildClient();
try {
graphClient.getMe().buildRequest().delete();
} catch (Exception e) {
Log.d(AccountSettingFragment.class.toString(), "Error deleting user. Error Details: " + e.getStackTrace());
}
return true;
}
Currently, my app fails when trying to get an access token with a null pointer exception:
com.microsoft.identity.client.exception.MsalClientException: Attempt to invoke virtual method 'long java.lang.Long.longValue()' on a null object reference
Any idea what I need to do to provide the user the option to users to delete their own account? Thank you!
Thanks for the help, #allen-wu. Due to his help, this azure feedback request and this azure doc, I was able to figure out how to get and delete users silently (without needing intervention).
As #allen-wu stated, you cannot have a user delete itself. So, I decided to have the mobile app call my server-side NodeJS API when the user clicks the 'Delete Account' button (as I do not want to store the client secret in the android app) and have the NodeJS API call the Azure AD endpoint to delete the user silently. The one caveat is that admin consent is needed the first time you try to auth. Also, I have only tested this for Graph API. I'm not a 100% sure if it works for other APIs as well.
Here are the steps:
Create your application in your AAD B2C tenant. Create a client secret and give it the following API permissions: Directory.ReadWrite.All ;
AuditLog.Read.All (I'm not a 100% sure if we need the AuditLog permission. I haven't tested without it yet).
In a browser, paste the following link:
GET https://login.microsoftonline.com/{tenant}/adminconsent?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&state=12345
&redirect_uri=http://localhost/myapp/permissions
Login using an existing admin account and provide the consent to the app.
Once you've given admin consent, you do not have to repeat steps 1-3 again. Next, make the following call to get an access token:
POST https://login.microsoftonline.com/{B2c_tenant_name}.onmicrosoft.com/oauth2/v2.0/token
In the body, include your client_id, client_secret, grant_type (the value for which should be client_credentials) and scope (value should be 'https://graph.microsoft.com/.default')
Finally, you can call the Graph API to manage your users, including deleting them:
DELETE https://graph.microsoft.com/v1.0/users/{upn}
Don't forget to include the access token in the header. I noticed that in Postman, the graph api had a bug and returned an error if I include the word 'Bearer' at the start of the Authorization header. Try without it and it works. I haven't tried it in my NodeJS API yet, so, can't comment on it so far.
#allen-wu also suggested using the ROPC flow, which I have not tried yet, so, cannot compare the two approaches.
I hope this helps!
There is a line of code: graphClient.getUsers("").buildRequest().delete();
It seems that you didn't put the user object id in it.
However, we can ignore this problem because Microsoft Graph doesn't allow a user to delete itself.
Here is the error when I try to do it.
{
"error": {
"code": "Request_BadRequest",
"message": "The principal performing this request cannot delete itself.",
"innerError": {
"request-id": "8f44118f-0e49-431f-a0a0-80bdd954a7f0",
"date": "2020-06-04T06:41:14"
}
}
}

Cannot load Google API Client on Ruby on Rails

I want to verify user profile from Google Authorization code sent by android client, to do that, we have to download client_secrets.json and put it inside our rails app. Just like this tutorial https://developers.google.com/identity/protocols/OAuth2WebServer
But when I try to follow this step
require 'google/apis/drive_v2'
require 'google/api_client/client_secrets'
client_secrets = Google::APIClient::ClientSecrets.load
auth_client = client_secrets.to_authorization
auth_client.update!(
:scope => 'https://www.googleapis.com/auth/drive.metadata.readonly',
:redirect_uri => 'http://www.example.com/oauth2callback',
:additional_parameters => {
"access_type" => "offline", # offline access
"include_granted_scopes" => "true" # incremental auth
}
)
Rails throws an error said "No client_secrets.json filename supplied and/or could not be found in search path."
The errors shows up even though I have insert client_secrets.json inside config/client_secrets.json
Do you know what's the problem or what's the alternative for this solution, thank you.
It seems, ClientSecrets.load accepts optional argument which is config filename path. So I believe it's okay if you specify filename directly:
Google::APIClient::ClientSecrets.load("#{Rails.root}/config/client_secrets.json")

Android switch from Parse to Parse Server crashes

I'm migrating my Android and iOS app from Parse.com to Parse Server. First I'm doing this migration with a test app to be sure everything is ok.
With iOS there has been no problem but with Android the updated app crashes each time the user enters in the app.
This is the Android code updated:
public static void initParse(Application app) {
ctxt = app.getApplicationContext();
Parse.enableLocalDatastore(app);
// Old version
//Parse.initialize(app, "<my app id>", "<my client key>");
// New version
Parse.initialize(new Parse.Configuration.Builder(ctxt)
.applicationId("<my app id>")
.clientKey(null)
.server("https://<my new server>/parse/")
.build()
);
ParseUser.enableRevocableSessionInBackground();
}
The crash returns a "com.parse.ParseException: java.lang.IllegalStateException: unable to encode an association with an unsaved ParseObject", and taking into account what I've read in Stackoverflow, this is because ParseUser.getCurrentUser() returns null (despite the user is logged in) and Parse methods fail.
In fact, if I take this same code, uncomment the old version of Parse.initialize, comment the new version of Parse.initialize, compile and launch the app, this is launched without problems and the user can use it properly.
But again, if I comment the old version of Parse.initialize, uncomment the new version of Parse.initialize, compile and launch the app, this crashes inmediately because ParseUser.getCurrentUser() returns null and Parse methods fail.
What am I missing here? Should I force my users to login again? This seems to me a bad UX, and anyway, in iOS I have no problems with the new version, what is the difference between both platforms?
EDIT 1:
Following Robert Rowntree advice I've changed the initialization to this:
public static void initParse(Application app) {
ctxt = app.getApplicationContext();
Parse.enableLocalDatastore(app);
// Old version
//Parse.initialize(app, "<my app id>", "<my client key>");
// New version
Parse.initialize(new Parse.Configuration.Builder(ctxt)
.applicationId("<my app id>")
.clientKey("<my client key>")
.server("https://<my new server>/parse/")
.build()
);
//Parse.setLogLevel(Parse.LOG_LEVEL_DEBUG);
Parse.setLogLevel(Parse.LOG_LEVEL_VERBOSE);
ParseUser.enableRevocableSessionInBackground();
}
Besides this I've checked that my Parse version is the last available, I have this in my gradle file:
compile 'com.parse.bolts:bolts-android:1.+'
compile 'com.parse:parse-android:1.+'
And the result is the same, the app crashes with "com.parse.ParseException: java.lang.IllegalStateException: unable to encode an association with an unsaved ParseObject" the first call I make to Parse.
Besides this, the unique log I can see related to parse is one related to GCM token, nothing else.
EDIT 2:
I've tried another thing, uninstall the app with the old initialization and install it with the new initialization and as I guessed the new version is now working.
But, of course this is not a solution to me, I can not tell my users to uninstall the app ans install it again.
But this is a hint: there is no problem with the new initialization but with how the old Parse "reacts" when opening the app with the new Parse Server, there must be any inconsistency in getCurrentUser or something like that.
Can you try initializing like this:
Parse.initialize(new Parse.Configuration.Builder(this)
.applicationId("APP_ID")
.clientKey("CLIENT_KEY")
.server("https://SERVER_URL/parse/") // The trailing slash is important.
.enableLocalDataStore()
.build()
);
notice the
.enableLocalDataStore()
is in the initilization. Remove this:
Parse.enableLocalDatastore(app);

Using gitskarios/GithubAndroidSdk

I'm trying to use the gitskarios/GithubAndroidSdk to provide my alpha and beta testers a way to view current github issues and report new ones. Here are the steps I've already taken:
Create a GitHub application at LINK.
Add the client info to my application's metadata:
com.alorma.github.sdk.client -> my client's ID
com.alorma.github.sdk.secret -> my client's secret
com.alorma.github.sdk.oauth -> my client's 'Authorization callback URL'
Run this code in my fragment's onCreate:
GithubDeveloperCredentials.init(new MetaDeveloperCredentialsProvider(mParent));
UserReposClient client = new UserReposClient(getActivity(), null);
client.setOnResultCallback(new BaseClient.OnResultCallback<List<Repo>>() {
#Override
public void onResponseOk(List<Repo> repos, Response response) {
Log.d("GitHub", "onResponseOK: " + response);
}
#Override
public void onFail(RetrofitError retrofitError) {
Log.d("GitHub", "onFail: " + retrofitError);
}
});
Log.d("GitHub", "getting repos");
client.execute();
The result is a single log for "getting repos" and no callback.
I've tried removing the call to GithubDeveloperCredentials.init() but that just causes a crash saying that provider is null. I then thought that maybe I didn't have an auth token so I made a call to the RequestTokenClient using null for the second parameter in RequestTokenClient(Context context, String code) but this too never gets a callback or return a null token if executed with executeSync().
UPDATE
I tried using the Personal access tokens (see comments) page to generate an access token then save it into StoreCredentials. When running the execute() command now it appears to be getting a little further but now I'm getting a new error saying:
retrofit.RetrofitError: LoginService.requestToken: HTTP method annotation is required (e.g., #GET, #POST, etc.).
Looking around I found an answer (see comments) saying this might be a proguard issue with the retrofit package. So I added those lines into my app's proguard file but no luck yet.
Can anybody help me? All I want to do is get a list of the open issues.
OK, so I got this working finally though not quite in the best way I wanted. I initially intended each of my Alpha testers to require having their own GitHub account and be added to the project then using their own credentials to obtain an auth token and accessing the bug list. Instead, using the auth token I generated in my update I was able to access the repo. I had to change the proguard to:
-keepattributes *Annotation*,Signature
-keep class retrofit.** { *; }
-keepclasseswithmembers class * {
#retrofit.http.* <methods>; }
(For some reason the *Annotation* and Signature nedded to be on the same line)

Persitent Error 580 Invalid Authentication Parameters with an Android App connecting to Sybase SUP

I am trying to register my native application to tye Sybase Control Center, but I always
get the Invalid Authentication Parameters. The problem is, that my Application and connection
profile disappeared from the SCC and I cant recreate it. Also, I created a new connection profile,
but the activation code is grayed out and blank, so I cannot restore it.
I already tried unregistering/registering, reinstalling the app on the emulator, but the Error 580 persists. I cannot declare the app on the SCC manually again, so I am now in a dead end, and without any fresh ideas.
Any suggestions? (below is my code, asume that the SCC is brand new, without any regsistered apps)
writeToLog("STARTING SYNC THREAD...");
app.setApplicationContext(ZServicesActivity.this); // context is the android.content.Context
//SMPNostrumDB.getSynchronizationProfile();
//-----------------------Connection Properties---------------------------
writeToLog("SETTING Connection PROPERTIES...");
ConnectionProperties connProps = app.getConnectionProperties();
connProps.setServerName(SERVER_172);
// connProps.setServerName(PUBLIC_SERVER);
connProps.setPortNumber(MSG_SERVER_PORT);
connProps.setActivationCode(ACTIVE_CODE);
connProps.setFarmId("0");
writeToLog("CONN Properties are SET.");
SMPNostrumDB.setApplication(app); // Set the android.content.Context for the application
LoginCredentials loginCredentials = new LoginCredentials(USER,PWD);
connProps.setLoginCredentials(loginCredentials);
writeToLog("Login Credentials, SET.");
if (app.getRegistrationStatus() != RegistrationStatus.REGISTERED) {
//app.unregisterApplication();
//writeToLog("...your nemesis");
writeToLog("REGISTERING App...");
connProps.setActivationCode(ACTIVE_CODE);
app.registerApplication();
writeToLog("APP REGISTERED...");
progressConnection.dismiss();
}
else{
writeToLog("App already created, CONNECTING...");
app.startConnection(10000);
writeToLog("APP CONNECTED...");
}
//-----------------------Connection Profile---------------------------
writeToLog("SETTING Connection PROFILE...");
ConnectionProfile connectionProperties=SMPNostrumDB.getSynchronizationProfile();
writeToLog(" (CP): APP DB and Server, are SET.");
connectionProperties.setServerName(SERVER_172);
connectionProperties.setPortNumber(SYNC_SERVER_PORT);
writeToLog("Connection PROFILE is SET.");
writeToLog("APP CONNECTED...");
progressConnection.dismiss();
app.setApplicationCallback(new MyApplicationCallback());
SMPNostrumDB.registerCallbackHandler(new MyCallbackHandler());
I am using SUP 2.3.3, and after a few days of waiting for a reply, a SAP consultant recommended me to leave out the activation code.
I removed all traces of the activation code from my android side code, and it WORKED! so I was able to move to close this chapter and move on with the app development.

Categories

Resources