Google Firebase how to catch specific Auth exception errors - Unity - firebase

-How to catch Auth exception errors - Unity
-How to catch if user/email exists - Unity
-Where to find the list of Auth exceptions error code - Unity
*I found a lot of answers for Android so I decided to finally write my solution for Unity.

The answer is simple- either use the following function on the task you are trying to achieve -
protected bool LogTaskCompletion(Task task, string operation)
{
bool complete = false;
if (task.IsCanceled)
{
Debug.Log(operation + " canceled.");
}
else if (task.IsFaulted)
{
Debug.Log(operation + " encounted an error.");
foreach (Exception exception in task.Exception.Flatten().InnerExceptions)
{
string authErrorCode = "";
Firebase.FirebaseException firebaseEx = exception as Firebase.FirebaseException;
if (firebaseEx != null)
{
authErrorCode = String.Format("AuthError.{0}: ",
((Firebase.Auth.AuthError)firebaseEx.ErrorCode).ToString());
}
Debug.Log("number- "+ authErrorCode +"the exception is- "+ exception.ToString());
string code = ((Firebase.Auth.AuthError)firebaseEx.ErrorCode).ToString();
Debug.Log(code);
}
}
else if (task.IsCompleted)
{
Debug.Log(operation + " completed");
complete = true;
}
return complete;
}
The printed output Debug.Log(code) is the exception code you are looking for. Now you can compare it - if (code.Equals("some specific exception....")) and complete it with your code.
Example:
How to catch if user/email exists
Let's say we sign up a new user with CreateUserWithEmailAndPasswordAsync and we want to catch the error " The email address is already in use"
We can use my function to find out what the error code we need to compare and it will print to output "EmailAlreadyInUse". Next all I need to do is to check if ((code).Equals("EmailAlreadyInUse"))
-Another possible way is to find the error code in a list-
List of Auth exceptions error code FOR UNITY
All the exceptions are under class Firebase.Auth.AuthError you can see them either on your code editor, or on Firebase website under - Unity - Firebase.Auth - Overview (under AuthError).
Hope it helps!

Related

GoogleBilling V5 + Volley on Android, getting NetworkUtility.shouldRetryException: Unexpected response code 403 (com.android.volley.AuthFailureError)

PROBLEM: I'm using GoogleBilling V5 to process in-app purchases. I use a Firebase firestore to store purchase tokens for verification. I am attempting to use Volley to check the store to see if a token exists. However, Volley never processes the POST method and instead returns with
E/Volley: [591] NetworkUtility.shouldRetryException: Unexpected response code 403 for https://MY_URL.cloudfunctions.net/verifyPurchases?PurchaseToken=REALLY_LONG_PURCHASE_TOKEN_STRING&purchaseTime=TIMESTAMP_LONG&orderId=ORDER_ID_STRING/
Where REALLY_LONG_PURCHASE_TOKEN_STRING, TIMESTAMP_LONG, and ORDER_ID_STRING look acceptable. That is, the token is being generated, the timestamp is appearing as a Long datatype (correct), and the OrderId is consistent with GoogleBilling.
SETUP:
Added dependencies in gradle
def billing_version = '5.0.0'
implementation "com.android.billingclient:billing:$billing_version"
def volley_version = "1.2.1"
implementation "com.android.volley:volley:$volley_version"
Verified that purchaseToken,purchaseTime,and orderId all match EXACTLY with what was updated to the the firebase functions.
Curent Firestore Rules are:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
Followed this tutorial
(Selling In-App Products on Android: Implementing Google Play Billing V4): https://www.youtube.com/watch?v=KYFM2z5KPq0
Migrated to V5 per Google's migration guide: https://developer.android.com/google/play/billing/migrate-gpblv5
I instantiate the BillingClient within my singleton class as follows. The verifyInAppPurchase function is called within the PurchasesUpdatedListener.
...
billingClient = BillingClient.newBuilder(mContext)
.enablePendingPurchases()
.setListener(new PurchasesUpdatedListener() {
#Override
public void onPurchasesUpdated(#androidx.annotation.NonNull BillingResult billingResult, #Nullable #org.jetbrains.annotations.Nullable List<Purchase> list) {
//Once we receive a response from Google, we have to verify whether or not the purchase has been made
//via our Firestore database
if(billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && list != null){
for(Purchase purchase : list){
if(purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED && !purchase.isAcknowledged()){
//Now that we've verified that there is indeed a purchase, we have to verify that the purchase token hasn't been used
// and is valid. We do this by storing all the purchase tokens in a database. New purchases won't be in the database yet.
verifyInAppPurchase(purchase);
}
}
}
}
}).build();
...
verifyInAppPurchase:
I'm almost positive the issue is in here somewhere.
Should I be using my app's SHA1 key somewhere to validate the client?
NOTE: When I just copy/paste the requestURL into a browser, I get a PERMISSION DENIED page
public void verifyInAppPurchase(Purchase purchase){
String requestURL = "https://MY_URL.cloudfunctions.net/verifyPurchases?"
+ "purchaseToken=" + purchase.getPurchaseToken() + "&"
+ "purchaseTime=" + purchase.getPurchaseTime() + "&"
+ "orderId=" + purchase.getOrderId() + "/";
Log.d("verifyInAppPurchase", "verifyInAppPurchase::RequestURL: " + requestURL); //This is as far as it gets. No other LOG messages appear (the onResponse Listener is never invoked)
StringRequest stringRequest = new StringRequest(
Request.Method.POST,
requestURL,
new Response.Listener<String>(){
#Override
public void onResponse(String response){
/* Once we made it in here, we know the user did *something* so we have to
* figure that out.
*/
try{
JSONObject purchaseInfoFromServer = new JSONObject(response);
Log.d("verifyInAppPurchase", "verifyInAppPurchase::Was the purchase valid?"); //never gets here
//Was the purchase valid?
if(purchaseInfoFromServer.getBoolean("isValid")){
//(yes!) Okay, cool. Well, we should acknowledge that (or we don't get paid)
Log.d("verifyInAppPurchase", "verifyInAppPurchase::Was the purchase valid? YES!");
AcknowledgePurchaseParams ackPurchaseParams = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
//So we acknowledge here and then set up a listener for when Google responds to our acknowledgement.
billingClient.acknowledgePurchase(
ackPurchaseParams,
new AcknowledgePurchaseResponseListener() {
#Override
public void onAcknowledgePurchaseResponse(#NonNull BillingResult billingResult) {
Log.d("verifyInAppPurchase", "verifyInAppPurchase::onAcknowledgePurchaseResponse: Acknowledged!");
//Google responded! Now, we have to deliver the goods!
if(billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK){
Log.d("verifyInAppPurchase", "verifyInAppPurchase::onAcknowledgePurchaseResponse: BillingResponseCode OK");
handlePurchase(purchase);
}
}
}
);
}
} catch (Exception e){
Log.d("verifyInAppPurchase", "verifyInAppPurchase::onResponse ERROR: " + e.toString());
}
}
},
new Response.ErrorListener(){
#Override
public void onErrorResponse(VolleyError error){
NetworkResponse response = error.networkResponse;
if (error instanceof ServerError && response != null) {
try {
String res = new String(response.data, HttpHeaderParser.parseCharset(response.headers, "utf-8"));
// Now you can use any deserializer to make sense of data
JSONObject obj = new JSONObject(res);
//Log.d("verifyInAppPurchase", "verifyInAppPurchase::Volley ERROR: " + obj.toString());
} catch (UnsupportedEncodingException e1) {
// Couldn't properly decode data to string
e1.printStackTrace();
//Log.d("verifyInAppPurchase", "verifyInAppPurchase::Volley ERROR: " + e1.toString());
} catch (JSONException e2) {
// returned data is not JSONObject?
e2.printStackTrace();
//Log.d("verifyInAppPurchase", "verifyInAppPurchase::Volley ERROR: " + e2.toString());
}
}
Log.d("verifyInAppPurchase", "verifyInAppPurchase::Volley ERROR: " + error.toString());
}
}
){
#Override
public String getBodyContentType(){
return "application/json";
}
};
// Volley should run on UI thread per
//https://google.github.io/volley/simple.html
Handler mainHandler = new Handler(mContext.getMainLooper());
Runnable myRunnable = () -> Volley.newRequestQueue(mContext).add(stringRequest);
mainHandler.post(myRunnable);
}
Incidentally, what changes will I have to make to ensure this is only ever accessed by my app? I'm currently thinking I'll have to change the rules to reference the 'auth' variable per (https://firebase.google.com/docs/database/security/rules-conditions), but that involves using the whole Firebase Authentication SDK which feels overkill. Is there a cleaner way of securing the function without forcing users to log in?

Why do i get "Exception:" when i receive the exception message in dart?

Hi I am new to the dart and flutter framework.
I am throwing error like below from my service class where i am calling the API.
if(res["status"]!=200) throw new Exception("Invalid User");
and receiving in presenter class like below and printing
.catchError((Object error){
print(error);
});
I am expecting the result as "Invalid User", but am getting with additional word like "Exception: Invalid User". Can you please explain me why this additional word 'Exception' is coming? and how can i get only the string which i am passing.
Surprisingly that's how written in exception toString code.
String toString() {
if (message == null) return "Exception";
return "Exception: $message"; // your message will be appended after exception.
}
If you want to avoid this then you have to create custom exception like below:
class HttpException implements Exception {
final String message;
HttpException(this.message); // Pass your message in constructor.
#override
String toString() {
return message;
}
}
You can use the message property of the Exception object
.catchError((error){
print(error.message);
});
This should output Invalid User
I hope this is what you are looking for.
Quick solution:
.catchError((error){
print(
error.toString().substring(11)
);
});
You cut the first 11 message chars, that is, "Exception: ".
You can create an extension function in dart as
extension FormattedMessage on Exception {
String get getMessage {
if (this.toString().startsWith("Exception: ")) {
return this.toString().substring(11);
}else {
return this.toString();
}
}
}
After that you can simply call
exception.getMessage;
to get your extension message without having to implement the Extension class or show the user how the exception is being substring.

Return JSON when an Unhandled error 500 exception is thrown

If there's an unhandled server error 500 in ASP.NET MVC, the server returns a HTML page like this:
Question: is it possible to configure the application so that it returns a JSON with the same information instead of the above HTML?
eg:
{
Title:'Maximum request length exceeded',
Description:'An unhandled eception .....',
...etc
}
you need to catch the error somehwere appropriate [i suggest use custom error filter on the controller for example that inherits from HandleErrorAttribute], and override OnException method, from there you can check if it is Ajax, then do something else, here is a snippet that i wrote before (not clean yet)
and dont forget to set the status code!
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
JsonResultObject result = new JsonResultObject();
result.Success = false;
string message = ("Common.WeAreFixing" + " , #" + errorLog.Id.ToString("00000"));
if (filterContext.HttpContext.Request.IsLocal)
{
message = filterContext.Exception.Message + Environment.NewLine + "st: " +
(filterContext.Exception.StackTrace ?? "");
}
result.AlertMessage = new Alert(message, Alert.Type.Error);
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
filterContext.Result = new JsonDotNetResult()
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = result
};
filterContext.HttpContext.Items["ErrorNumber"] = errorLog.Id.ToString("00000");
}
Within exceptions there is sensitive information including stack details that should not be leaked to the consumer however as you are showing the error screen i am presuming that this is running in a debug environment and that is not an issue.
Also in a non debug environment the exception details may be stripped out of the response so ideally you should form a custom message of Json format that is based off that exception and then log the original stack details so you can handle the issue at a later date.
Something like the below should get you started:
try
{
// do some work
}
catch (ExplicitException ex)
{
// Log the exception(ex);
var message = "Error Doing Work");
return Json(new { status = "Error", message });
}
}

Xamarin.iOS push notification tag .NET API / Azure Notification Hub

We're developing a cross platform (Android and iOS) application using Xamarin.Forms. Up to now, we managed to get the app working fine, so it's cool !
We've included some push notifications in our app, using Azure Notification Hub, GCM (for android) and APNS (for iOS). And it works almost fine !
Actually, we just have a last problem : everything is OK for Android, and we can register to push notifications using iOS too, but we can't add some tags to our registrations.
Indeed we need to be able to send a push notification to one user, or one group of users instead of to everyone. To do this, we are doing that in a method of our web api :
if (user.DeviceType.Equals("Android"))
{
registration = new GcmRegistrationDescription(handles.Handle);
}
else
{
registration = new AppleRegistrationDescription(handles.Handle);
}
registration.Tags = new HashSet<string>();
registration.Tags.Add("usermail:" + user.Email);
registration.Tags.Add("userid:" + user.Id);
registration.Tags.Add("userdevice:" + user.DeviceType);
registration.Tags.Add("usertype:" + tag);
registration.RegistrationId = handles.RegistrationId;
await NotificationHelper.Hub.CreateOrUpdateRegistrationAsync(registration);
And for the given Handle, we retrieve it this way in Android :
protected override void OnRegistered(Context context, string registrationId)
{
[...] //the registration id is given in args
}
and this way in iOS :
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
[...]
var DeviceToken = deviceToken.Description;
if (!string.IsNullOrWhiteSpace(DeviceToken))
{
DeviceToken = DeviceToken.Trim('<').Trim('>');
}
UserInformations.Handles.RegistrationId = DeviceToken.Replace(" ", "").ToUpper();
[...]
}
Everything works fine in Android (I'm able to add tags) but I got an error for iOS. The line
await
NotificationHelper.Hub.CreateOrUpdateRegistrationAsync(registration);
is generating an exception, telling me that my registrationId is "not valid anymore". You can notice that I remove spaced in my registrationId for iOS because if I don't, I got another error, telling me that my registrationID contains non-hexadecimal characters.
I don't know what to do to fix this, do I retrievea wrong registrationId in iOS, or is the way to add tags differents for APNS ?
Thanks for your help !
EDIT : I noticed that the device token has to be in uppercase. But surprisingly enough, I got the same error. Here are 2 screenshots to help you understand :
So you can see that in my registration, what I got in DeviceToken and what I got in RegistrationId are the same... I don't know what to do :/
There's actually a lot of documentation and thread posts online that tell you to adjust the device token that you get from the iOS method 'RegisteredForRemoteNotifications'. However if you look at the official documentation this is not the correct way of doing it.
Below is a snippet from our 'RegisteredForRemoteNotifications' method, as you can see we don't do anything with the device token, give it a shot and let me know if this solves your problem.
if (oldDeviceToken != null)
{
if (oldDeviceToken.ToString() != deviceToken.ToString())
{
try
{
Hub.UnregisterAllAsync(oldDeviceToken, (error) =>
{
//check for errors in unregistration process.
if (error != null)
{
TestingLogs.ApplicationLog.AppendFile(DateTime.Now.ToString() + " : " + "[PNS EXCEPTION] - Exception has been hit! - Message: " + error + " | Source: " + "Unregistering old device token against the notification hub.");
//exit out of the code here because we can't keep our hub clean without being able to remove the device from our registration list.
return;
}
else
{
ShouldComplete = true;
}
});
}
catch (Exception genEx)
{
TestingLogs.ApplicationLog.AppendFile(DateTime.Now.ToString() + " : " + "[PNS EXCEPTION] - Exception has been hit! - Message: " + genEx.Message + " | Source: " + genEx + Environment.NewLine + Environment.NewLine);
}
}
}
else
{
// Store current device token
bool res = await ApplicationSettings.StoreDeviceToken(deviceToken);
}
// Check if we need to perform our initial registrations
if (ShouldComplete)
{
NSSet RegisteredTags = await ApplicationSettings.RetrieveUserTags();
if (RegisteredTags == null)
{
RegisteredTags = new NSSet("AppleDevice");
}
//Register the device against the notification hub keeping the details accurate at all times.
Hub.RegisterNativeAsync(deviceToken, RegisteredTags, (errorCallback) =>
{
if (errorCallback != null)
{
TestingLogs.ApplicationLog.AppendFile(DateTime.Now.ToString() + " : " + "[PNS EXCEPTION] - Exception has been hit! - Message: " + errorCallback + " | Source: " + "Registering device token against the notification hub.");
}
else
{
if (deviceToken != null)
{
NSUserDefaults.StandardUserDefaults.SetString("Completed", "InitialTagRegistration");
NSUserDefaults.StandardUserDefaults.Synchronize();
}
}
});
}

Error for GetStringAsync if triggered by ScheduledAgent but no error during WP8 App usage

I have a wrapper for the webclient that I am using to retrieve some data. This same function is being used by the WP8 App and also used by the WP8 ScheduledAgent.
Somehow, when the function is used by the WP8 App, there is no error and it returns correctly.
However, when the ScheduledAgent uses the function, it erred out at the bold code below. I tried a try catch but it is not catching. Via Debugger, the GetSTringAsync(uri) had completed without any exception. The error seemed to be only happening when it is assigning the return Task to the result string.
The error I received is:
An unhandled exception of type 'System.UnauthorizedAccessException' occurred in System.Windows.ni.dll
public class HttpClient : WebClient
..
private async Task GetStringAsync(string strUri)
{
Uri uri = new Uri(strUri);
string result = string.Empty;
try
{
result = await GetStringAsync(uri);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return result;
}
...
private Task GetStringAsync(Uri requestUri)
{
TaskCompletionSource tcs = new TaskCompletionSource();
try
{
this.DownloadStringCompleted += (s, e) =>
{
if (e.Error == null)
{
tcs.TrySetResult(e.Result);
}
else
{
tcs.TrySetException(e.Error);
}
};
this.DownloadStringAsync(requestUri);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
if (tcs.Task.Exception != null)
{
throw tcs.Task.Exception;
}
return tcs.Task;
}
Please advise if I am missing something.
My problem is because I am using pushpin as one of my object types within my Model. Apparently, in the scheduled agent, it is not able to access that object type and thus threw the above error.

Resources