No ChallengeStatusReceiver for transaction id - android

My React Native app is crashing when some users try to add a payment method in production. This is happening on Android only. I cannot reproduce it in debug. I'm getting this mysterious error report in Sentry and after some Googling I have no idea what this error means:
No ChallengeStatusReceiver for transaction id
d.q.a.g1.i.d: No ChallengeStatusReceiver for transaction id
at d.q.a.g1.l.s.a
at d.q.a.g1.l.q.c
at d.q.a.g1.l.q.b
at d.q.a.g1.l.y$a.onPostExecute
at android.os.AsyncTask.finish(AsyncTask.java:755)
at android.os.AsyncTask.access$900(AsyncTask.java:192)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:772)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:237)
at android.app.ActivityThread.main(ActivityThread.java:8167)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:496)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1100)
I'm using tipsi-stripe and I'm pretty sure this error is happening when calling stripe.confirmSetupIntent(). This method presents an authentication "challenge" if the user's bank requires it.
The flow I'm using now is:
user enter payment details:
const createPaymentMethodParams = {
"billingDetails":{
"name":"First Last"
},
"card":{
"cvc":"333",
"expMonth":12,
"expYear":23,
"number":"4111111111111111"
}
}
Call the backend, which creates a setup intent with confirmation_method=manual. This returns a client_secret
"Create" the payment method with createPaymentMethod, which returns payment method id.
const paymentMethodData = await
stripe.createPaymentMethod(createPaymentMethodParams)
Finally, confirm the setup intent (this is the step which sometimes crashes):
// confirm the setup intent:
// - if authentication is required user will be redirected to auth flow
// - if no authentication is required this directly returns paymentMethodId
const { status, paymentMethodId } = await stripe.confirmSetupIntent({
clientSecret: intent.client_secret, // previously obtained from backend
paymentMethodId: paymentMethodData.id,
})
The most relevant result I found on Google was this https://adyen.github.io/adyen-3ds2-android/com/adyen/threeds2/ChallengeStatusReceiver.html
It refers to a 3DS sdk (as in "3-D Secure"), which would make sense to see at this stage when the user is required to authenticate. However I'm really not sure I'm on the right track...
Has anyone encountered this issue and can help me understand it, or point me in a direction? Any help greatly appreciated!

Related

How to find out if app has been disconnected from Google Drive?

I'm building an app where I store app data in the app-specific-folder on Google Drive. I've been able to setup everything related to file storage and retrieval. The problem I'm facing is regarding permissions. The user has an option to disconnect the app from the Google Drive settings panel.
I use the DriveScopes.DRIVE_APPDATA meaning https://www.googleapis.com/auth/drive.appdata scope to save data.
I'm trying to figure out how to find out if this has happened on the app side. If I try to continue using the drive related apis, with the app being disconnected, then it crashes with a UserRecoverableAuthException.
com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException
at com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential$RequestHandler.intercept(GoogleAccountCredential.java:297)
at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:868)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:476)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:409)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:526)
at abhiank.maplocs.ui.drivesync.DriveSyncService.onHandleIntent(DriveSyncService.kt:68)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:78)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.os.HandlerThread.run(HandlerThread.java:67)
Caused by: com.google.android.gms.auth.UserRecoverableAuthException: NeedPermission
at com.google.android.gms.auth.zze.zzb(Unknown Source:13)
at com.google.android.gms.auth.zzd.zza(Unknown Source:77)
at com.google.android.gms.auth.zzd.zzb(Unknown Source:20)
at com.google.android.gms.auth.zzd.getToken(Unknown Source:7)
at com.google.android.gms.auth.zzd.getToken(Unknown Source:5)
at com.google.android.gms.auth.zzd.getToken(Unknown Source:2)
at com.google.android.gms.auth.GoogleAuthUtil.getToken(Unknown Source:55)
at com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential.getToken(GoogleAccountCredential.java:267)
at com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential$RequestHandler.intercept(GoogleAccountCredential.java:292)
I tried the following to figure out if the app does not have the permissions or scopes.
Look at data inside GoogleSignInAccount instance received from GoogleSignIn.getLastSignedInAccount(this). This had the following scopes available in account.grantedScopes() after app had been disconnected. drive.appdata is shown even though app is disconnected.
[https://www.googleapis.com/auth/drive.appdata, https://www.googleapis.com/auth/userinfo.profile, https://www.googleapis.com/auth/userinfo.email, openid, profile, email]
Last thing I tried was hasPermissions method available in GoogleSignIn. I checked if the APP_DATA scope was available with this call and it returned true. So no help there either.
GoogleSignIn.hasPermissions(account, Scope(DriveScopes.DRIVE_APPDATA))
I'm really stuck now. Any help will be really appreciated. Thanks.
I ended up using a try-catch around my drive related code and catching the UserRecoverableAuthIOException. According to the documentation, this is called when -
UserRecoverableAuthExceptions signal Google authentication errors that
can be recovered with user action, such as a user login.
This has worked decently well for me. Considering the fact that this question has not received any other answers in 2 years, there doesn't seem to be any method to fetch the information about whether the app is disconnected or not via an API or SDK call.
Here's the code I use
fun getGoogleDriveService(context: Context): Drive {
val credential = GoogleAccountCredential.usingOAuth2(context, setOf(DriveScopes.DRIVE_APPDATA))
credential.selectedAccount = GoogleSignIn.getLastSignedInAccount(context)!!.account
return Drive.Builder(NetHttpTransport(), GsonFactory(), credential)
.setApplicationName(DriveSyncService.APP_NAME)
.build()
}
try {
val driveService = getGoogleDriveService(this)
var fileList = driveService.files().list().execute()
//...
//more code
} catch (e: UserRecoverableAuthIOException) {
/*
Doing a sign-out on the googleSignInClient so that there is no mismatch
in sign-in state and so that when I start sign-in process again, it
starts afresh
*/
googleSignInClient.signOut()
/*
Then I show a pop up telling user that app was disconnected and
to sign in again. And then on click I start the sign-in flow again.
*/
} catch (e: GoogleJsonResponseException) {
//https://googleapis.dev/java/google-api-client/latest/index.html?com/google/api/client/googleapis/json/GoogleJsonResponseException.html
//404 is file being updated/deleted was not found
if (e.message != null && e.message!!.contains("storageQuotaExceeded")) {
//todo handle storage exceeded error. Inform user
}
} catch (e: IOException) {
//todo handle network error
}

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"
}
}
}

React-Native Firebase: Token refresh not working

I have an issue with react-native-firebase (or firebase) in which my app does not receive a trigger after the auth token refreshes. It's pretty much the same issue as [1], but they never posted a solution.
So, what happens is that both on an Android phone and on the Android emulator (no idea about iOS), signing up, logging in and logging out works perfectly, meaning the listeners correctly see when I do a logout() etc. But the listeners never fire when the token refreshes.
My first question is: Am I correct to assume that the onIdTokenChanged-listener should automatically fire after 60 minutes without having to do anything else, e.g. call any firebase function, such that the app just sits there doing nothing for 60 minutes and then receiving the event and replacing the token?
My main component which contains the listeners looks like this:
class ReduxAppWrapper extends Component {
componentDidMount() {
firebase.auth().onAuthStateChanged((user) => {
console.log('COMP DID MOUNT: AUTH STATE CHANGED! ' + JSON.stringify(user));
});
firebase.auth().onIdTokenChanged((user) => {
console.log('COMP DID MOUNT: TOKEN CHANGED! ' + JSON.stringify(user));
});
firebase.auth().onUserChanged((user) => {
console.log('COMP DID MOUNT: USER CHANGED! ' + JSON.stringify(user));
});
};
render() {
return (
<ReduxProvider store={store}>
<MenuProvider>
<PaperProvider>
<AppContainer />
</PaperProvider>
</MenuProvider>
</ReduxProvider>);
}
}
Normally inside the listener I have a function that dispatches a redux-action such that the authentication information is broadcast across my components. Inside those components I use the jwt token for http-requests to my backend.
Now the backend of course uses firebase to validate that token (and this is where the problem occurs after the 60 minutes since it retrieves an outdated jwt), but I think I am right to assume that the problem lies within the app since the refresh does not happen.
I'd be really glad if someone could point me to where to look, I also tried to find out in the firebase console whether a token refresh event was sent, but I could not find anything about that.
So basically:
1) Am I right to assume that the firebase.auth().onIdTokenChanged() function should be called without me doing anything else? Or is it not enough to define the listener once in the main component (also regarding the fact that other screens will be rendered on top of that due to the stack-nvigation).
2) If the code is fine, do you have any hints for where to look?
Thanks so much!
[1] https://github.com/invertase/react-native-firebase/issues/531
For anyone with the same issue, I ended up asking firebase asking for the token everytime I needed it. I still think this should not be necessary but I did not want to spend any more time analyzing why the refresh did not work automatically. So what I am doing in the app is
firebase.auth().currentUser.getIdToken().then((token) => {
fetch(url, {
method: 'GET',
headers: { Authorization: token }
})
}
¯\_(ツ)_/¯
Apparently, with getIdToken, Firebase only makes a call to its server to get a new token if the current token has expired; it does not create unnecessary requests if it does not have to.
Quite a crucial detail which can be confusing if you are not aware of it and makes you (rightfully) assume that onIdTokenChanged is a listener which you would need to use to automatically update the token ...
https://firebase.google.com/docs/reference/js/firebase.User.html#getidtoken
Returns the current token if it has not expired. Otherwise, this will refresh the token and return a new one.

PayPal Here Android SDK Error - BadConfiguration: Cannot proceed with this merchant account

I'm trying to integrate the PayPal Here swipers into a Xamarin Android app. Everything is fine until I try and give my credentials to the SDK. Specifically, the line containing the call to PayPalHereSDK.SetCredentials
public void InitializeSdk( Context context, string serverName, string accessToken, string refreshUrl, string expires, IPayPalHereSdkWrapperCallback listener ) {
PayPalHereSDK.Init( context, serverName );
PayPalHereSDK.RegisterAuthenticationListener( this );
PayPalHereSDK.CardReaderManager.RegisterCardReaderConnectionListener( this );
if ( !string.IsNullOrEmpty( accessToken ) ) {
var credentials = new OAuthCredentials( accessToken, refreshUrl, expires );
PayPalHereSDK.SetCredentials( credentials, new SetAccessTokenResponseHandler( listener ) );
}
}
My SetAccessTokenResponseHandler class implements the Com.PayPal.Merchant.Sdk.Domain.IDefaultResponseHandler interface. As described above, the OnError function is called when call the PayPalHereSDK.SetCredentials function. I'm given the error code "BadConfiguration" and the message "Cannot proceed with this merchant account. ready"
I've searched Google high and low and, I believe, scoured SO pretty thoroughly. I can't seem to overcome the error, so I'm asking for help!
I think the paypal email is not verified properly. Please go through the merchant onboarding guide document to get more details regarding making the merchant eligible.
https://github.com/paypal/paypal-here-sdk-android-distribution/blob/master/docs/Merchant%20Onboarding%20Guide.pdf
Hope this helps. Cheers.
I'm not sure what exactly the issue was, but I ended up deleting the Sandbox App in my PayPal dev portal and creating a new one. Everything works now. head scratch
I resorted to this because, while trying to follow Sundar's suggestion, I started getting an "invalid scope" error. I had received them before and KNEW I had it fixed ( and no code had changed ). When I deleted/recreated the app, that error went away. Frustrating, but that's what worked!

Phonegap facebook plugin for android gives me an oauthexception

seriously going insane here....
I'm trying to get the phonegap facebook plugin for android to work, but it's really driving me up the wall (no pun intented).
I am using the code from https://github.com/irnc/phonegap-plugin-facebook-connect/tree/oauth-2.0+irnc, at least I think I am.
I appear to have two problems:
the following callback in the login (from pg-plugin-fb-connect) gives an error because "FB.Auth.setAuthResponse(response.authResponse, response.status);" cannot be found. Am I using an incorrect facebook sdk? Apparently no, see edit below
PhoneGap.exec(function (response) {
console.log('PG.FB.login.success: ' + JSON.stringify(response) + ', store into localStorage ...');
localStorage.setItem(key, JSON.stringify(response));
FB.Auth.setAuthResponse(response.authResponse, response.status);
if (cb) {
cb(response);
}
}, null, service, 'login', ['publish_stream', 'read_stream']);
},
When I comment the FB.Auth.setAuthResponse(response.authResponse, response.status); statement, my login returns successfull! I get an authresponse with an accesstoken and status set to connected. When I try to execute the following code (on success callback)
FB.api('/me/feed', 'post', { message: body }, function(response) {
if (!response || response.error) {
console.log(JSON.stringify(response.error, null, 4));
alert('We are very sorry, but somthing went wrong');
} else {
alert('Message was successfully posted to your wall!');
}
});
it gives me an oauthexception message: "An active access token must be used to query information about the current user."
I authenticated with 'read_stream, publish_stream' permissions.
These two are probably related, but I can't find anything about the setAuthReponse call in the facebook api.
EDIT help is apparently not on it's way, but i've continued my quest to get this to work.
The facebook js sdk I got from the github repo's are all using the 'old' auth methods. I've downloaded the new facebook js sdk and FB.Auth.setAuthResponse is there. I copied the code to my existing js sdk and changed all calls to setSession to setAuthRepsonse. Everything is working fine, except that the access token doesn't appear to be posted when I make above FB.api calls. After these changes, the error remains exactly the same!
Oh yeah, I also changed the check in the login callback to check for authResponse instead of session (it's in the example).
Help is more than welcome,
rinze
I think I fixed this. Basically the ConnectPlugin.java is still returning a "session" response object instead of the "authResponse" that the new SDK expects.
See https://github.com/odbol/phonegap-plugin-facebook-connect/commit/0ef84e29603338930ff82fc6d6ef8525b668077d for details.

Categories

Resources