Receiving firebase FCM messages multiple times in Xamarin - c#

I'm trying to integrate Firebase FCM into my app but i'm receiving messages
multiple times.
I send the messages trough a cloud function that triggers whenever a notice is added to the database like this:
import { DataSnapshot } from "firebase-functions/lib/providers/database";
import { EventContext } from "firebase-functions";
import * as admin from 'firebase-admin'
import { ResolvePromise } from "./misc";
export function doSendNoticeFCM(snapshot: DataSnapshot, context?: EventContext) {
const uid = context.params.uid;
const noticeid = String(context.params.noticeid);
const notice = snapshot.val();
return admin.database().ref('device-tokens').child(uid).child('0')
.on('value', (data) => {
const token = data.val();
if (token === null) {
return ResolvePromise();
}
const title = String(notice['Title']);
const body = String(notice['Body']);
console.log("Title: " + title);
console.log("Body: " + body);
const payload: admin.messaging.Message = {
data: {
notice_id: noticeid,
title: title,
body: body
},
android: {
ttl: 0
},
token: token
};
return admin.messaging().send(payload)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
});
}
This works fine i retrieve the device token, send the message and i receive it in my app in my messaging service.
using System;
using Android.App;
using Android.Support.V4.App;
using Firebase.Messaging;
using Android.Util;
using Doshi.Xamarin.Abstractions.StaticData;
using Android.Content;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
using Android;
using Xamarin.Forms;
using Plugin.CurrentActivity;
using Acr.UserDialogs;
using Doshi.Xamarin.Core.Helpers;
using Doshi.Xamarin.Abstractions.Misc;
using Doshi.Xamarin.Android.Logic.Interfaces;
using Doshi.Xamarin.Android.Logic.Implementations;
namespace Doshi.Droid
{
[Service(Name = "com.doshi.droid.DoshiMessagingService")]
[IntentFilter(new[] {"com.google.firebase.MESSAGING_EVENT"})]
public class DoshiMessagingService : FirebaseMessagingService
{
INoticePresenter _noticePresenter = new DoshiNoticePresenter();
public override void OnMessageReceived(RemoteMessage message)
{
HandleNotice(message);
}
private void HandleNotice(RemoteMessage message)
{
int id = DateTime.Now.Millisecond;
//Create the hardware notice.
_noticePresenter.PresentNotice(this, message, id, Xamarin.Droid.Resource.Drawable.ic_logo, typeof(MainActivity));
}
}
The problem occurs when i log out of my app and then login again the same notices i received earlier are received again. I use google authentication with firebase in my app and i remove the device token from the database when i log out and add the current token when i login again. Could this be the problem?
from what i can see in the firebase logs the cloud function is only executed once for each message so i'm guessing somethings wrong on the client side. I read on a other stackoverflow post that setting ttl to 0 would resolve this issue but it's not effecting anything what i can see.
Has anybody else run into this issue or have any idea of what i'm doing wrong?
I'm using the latest "stable" version of the Xamarin.Firebase.* nugets.

Found my issue. I should use "once" instead of "on" in my firebase function which explains why it was sent multiple times as my listener was triggered when i add/removed device tokens

Related

I am trying to send an email in a C# mvc app and I get Error message: Unable to get IAM security credentials from EC2 Instance Metadata Service

I have a app that uses boiler plate code from aws ses docs https://docs.aws.amazon.com/ses/latest/dg/send-an-email-using-sdk-programmatically.html. This is my EmailSender.cs file:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Amazon;
using Amazon.Runtime;
using Amazon.SimpleEmail;
using Amazon.SimpleEmail.Model;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
namespace amaranth.Helpers
{
public class EmailSender : IEmailSender
{
private ApiEndpoints _endpoints;
public EmailSender(IOptions<ApiEndpoints> _options)
{
_endpoints = _options.Value;
}
public async Task SendEmailAsync(string email, string subject, string htmlMessage)
{
using (var client = new AmazonSimpleEmailServiceClient(RegionEndpoint.USEast1))
{
var sendRequest = new SendEmailRequest
{
Source = "<A DOMAIN THAT I DO OWN>",
Destination = new Destination
{
ToAddresses =
new List<string> { email }
},
Message = new Message
{
Subject = new Content(subject),
Body = new Body
{
Html = new Content
{
Charset = "UTF-8",
Data = htmlMessage
}
}
},
// If you are not using a configuration set, comment
// or remove the following line
//ConfigurationSetName = configSet
};
try
{
Console.WriteLine("Sending email using Amazon SES...");
var response = await client.SendEmailAsync(sendRequest);
Console.WriteLine("The email was sent successfully.");
}
catch (Exception ex)
{
Console.WriteLine("The email was not sent.");
Console.WriteLine("Error message: " + ex.Message);
}
}
}
}
}
My variables email, subject and htmlMessage are all properly formatted. Also I verified the domain (represented with <A DOMAIN THAT I DO OWN> in the code). See here:
I am also out of the sandbox and I even created an IAM user to handle email:
But when I run my app, the email isn't sent and I get this error: Error message: Unable to get IAM security credentials from EC2 Instance Metadata Service.
I am not sure what the problem is but I noticed that I'm not really declaring my credentials anywhere. But... I don't even know where I would put the credentials. The amazon ses docs don't really appear to specify where the credentials go. Can somebody show me where I'm going wrong and where my credentials go?
UPDATE:
I added a BasicAWSCredentials where _endpoints.EmailUsername is my "Access key ID" and _endpoints.EmailPassword" is my Secret Access Key for the user I created specifically for this app.
var awsCreds = new BasicAWSCredentials(_endpoints.EmailUsername, _endpoints.EmailPassword);
using (var client = new AmazonSimpleEmailServiceClient(awsCreds, RegionEndpoint.USEast1))
But I got this error:
Error message: The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.
I was able to get this working. It was a strange fix. First I added my credentials to the code:
AWSCredentials awsCreds = new BasicAWSCredentials(_endpoints.EmailUsername, _endpoints.EmailPassword);
using (var client = new AmazonSimpleEmailServiceClient(awsCreds, RegionEndpoint.USEast2))
But then I got this error: The request signature we calculated does not match the signature you provided. This was the difficult error to get around. I tried a lot of stuff but then I came across this post https://adithya.dev/aws-signaturedoesnotmatch-error/. I generated another credential for my aws user that had a + closer to the end of the name and then it worked perfectly. I think this is a weird bug in the C# (and possibly other languages) AWS code.

Azure Function Post C# not executing with big body

I'm currently implementing an Azure Function App that exposes a few Functions (mostly gets).
The following code seems to have an issue:
using System;
using System.Collections.Specialized;
using System.Net;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using System.Net.Http;
using InternalVacanciesAzureFunction.Model;
using Microsoft.Extensions.Options;
using System.Text.Json;
using System.Collections.Generic;
namespace IVAFunction
{
public class PostFunction
{
private readonly HttpClient _httpClient;
public PostFunction(IHttpClientFactory httpClientFactory)
{
_httpClient = httpClientFactory.CreateClient();
}
[Function("postFunction")]
public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "post", "put")] HttpRequestData req,
FunctionContext executionContext)
{
ILogger logger = executionContext.GetLogger("PostFunction");
logger.LogError("Code hit: PostFunction.cs");
HttpResponseData response = req.CreateResponse();
string body = new System.IO.StreamReader(req.Body).ReadToEnd();
JsonResponse data = Nfunction.postFunction(_httpClient, "/PostFunction", body, logger, requestPrincipalName);
if (data.responseType.Equals(ResponseType.OK))
{
response.StatusCode = HttpStatusCode.OK;
}
else
{
response.StatusCode = HttpStatusCode.InternalServerError;
}
response.Headers.Add("Content-Type", "application/json; charset=utf-8");
response.WriteString(data.json);
return response;
}
}
}
The data that is posted to this function is a JSON including a BASE64 encoded string in two of the fields. The max size for each of those two fields is 1.5MB. Everytime I post something small e.g. 2 x 400B, everything goes fine. But when I sent something like 2 x 900kB the logging show up like this:
2021-12-07T07:42:28.455 [Debug] Request successfully matched the route with name 'postFunction' and template 'api/postFunction'
2021-12-07T07:42:29.129 [Information] Executing 'Functions.postFunction' (Reason='This function was programmatically called via the host APIs.', Id=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx)
The "Code hit" logger code is never hit and after a while the function times out.
Anyone having a clue what is going on? I can reproduce the issue on both my local dev environment as well on actual Azure.
You got a lot of missing directives or assembly references there
except for that the last } is unnenecessary.

Sending text message to phone using twilio

I am trying to send text message to phone. Can someone tell me why my return statement is not working? If i write only string message in my return statement then it shows that message but if i use below mentioned return statement it doesn't work. Any suggestions?
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using Twilio;
using Twilio.AspNet.Mvc;
using Twilio.Rest.Api.V2010.Account;
using Twilio.Types;
namespace TwilioSendSMS.Controllers
{
public class SMSController : TwilioController
{
// GET: SMS ----- outbound----
public ActionResult SendSms()
{
// Find your Account Sid and Auth Token at twilio.com/user/account
const string accountSid = "ACxxxxxxxxx";
const string authToken = "71xxxxxxxxxx";
// Initialize the Twilio client
TwilioClient.Init(accountSid, authToken);
// make an associative array of people we know, indexed by phone number
var people = new Dictionary<string, string>() {
{"+18180000000", "Kim"},
{"+14401112222", "Raj"}
};
// Iterate over all our friends
foreach (var person in people)
{
// Send a new outgoing SMS by POSTing to the Messages resource
MessageResource.Create(
from: new PhoneNumber("+15005550006"), // From number, must be an SMS-enabled Twilio number
to: new PhoneNumber(person.Key), // To number, if using Sandbox see note above
// Message content
body: $"Hey {person.Value} Party is at 6PM! Don't forget to bring gift.");
}
//return Content($"Message has been sent!");
return Content($"Sent message to {person.Value}");
}
}
}
Below is the working code!
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using Twilio;
using Twilio.AspNet.Mvc;
using Twilio.Rest.Api.V2010.Account;
using Twilio.Types;
namespace TwilioSendSMS.Controllers
{
public class SMSController : TwilioController
{
// GET: SMS ----- outbound----
public ActionResult SendSms()
{
// Find your Account Sid and Auth Token at twilio.com/user/account
const string accountSid = "ACxxxxxxxxx";
const string authToken = "71xxxxxxxxxx";
// Initialize the Twilio client
TwilioClient.Init(accountSid, authToken);
// make an associative array of people we know, indexed by phone number
var people = new Dictionary<string, string>() {
{"+18180000000", "Kim"},
{"+14401112222", "Raj"}
};
// Iterate over all our friends
var name ="";
foreach (var person in people)
{
// Send a new outgoing SMS by POSTing to the Messages resource
MessageResource.Create(
from: new PhoneNumber("+15005550006"), // From number, must be an SMS-enabled Twilio number
to: new PhoneNumber(person.Key), // To number, if using Sandbox see note above
// Message content
body: $"Hey {person.Value} Party is at 6PM! Don't forget to bring gift.");
name = $"{name} {person.Value}";
}
return Content($"Sent message to {name}");
}
}
}

ClaimsPrincipal.Current.Identity.Name Empty when authenticated from client, fine in browser

I have the following Azure Function,
#r "Newtonsoft.Json"
using Newtonsoft.Json.Linq;
using System.Net;
using System.Security.Claims;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
try
{
JObject pJOtClaims = new JObject();
foreach(Claim curClaim in ClaimsPrincipal.Current.Identities.First().Claims)
{
pJOtClaims.Add(curClaim.Type, new JValue(curClaim.Value));
}
return(req.CreateResponse(HttpStatusCode.OK, $"{pJOtClaims.ToString(Newtonsoft.Json.Formatting.None)}"));
}
catch(Exception ex)
{
return(req.CreateResponse(HttpStatusCode.OK, $"{ex.Message}"));
}
}
I have configured only Facebook authentication for this Function App. This function works for both in-browser and client authentication. When I invoke this method in browser I get a whole bunch of claims, including my registered Facebook email address. When I invoke this from client authentication, I get the following claims,
{
"stable_sid":"...",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier":"...",
"http://schemas.microsoft.com/identity/claims/identityprovider":"...",
"ver":"...",
"iss":"...",
"aud":"...",
"exp":"...",
"nbf":"..."
}
Unfortunately none of these include my Facebook email address which I need. I have enabled the "email" scope for the Facebook authentication configuration. Any ideas how to get this?
Nick.
Okay so I haven't found the exact solution I wanted, but this should get me by. Technically I only need the email address during registration, after that I can just use the stable_sid as is part of the identity I do get. So What I have done is to pass on the x-zumo-auth header to the ".auth/me" method, get the property I need. I'm using this method
public static async Task<String> GetAuthProviderParam(String iAuthMeURL,
String iXZumoAUth,
String iParamKey)
{
using (HttpClient pHCtClient = new HttpClient())
{
pHCtClient.DefaultRequestHeaders.Add("x-zumo-auth", iXZumoAUth);
String pStrResponse = await pHCtClient.GetStringAsync(iAuthMeURL);
JObject pJOtResponse = JObject.Parse(pStrResponse.Trim(new Char[] { '[', ']' }));
if(pJOtResponse[iParamKey] != null)
{
return (pJOtResponse[iParamKey].Value<String>());
}
else
{
throw new KeyNotFoundException(String.Format("A parameter with the key '{0}' was not found.", iParamKey));
}
}
}
This can be called in the function like so,
if(req.Headers.Contains("x-zumo-auth"))
{
String pStrXZumoAuth = req.Headers.GetValues("x-zumo-auth").First();
String pStrParam = await FunctionsHelpers.GetAuthProviderParam("https://appname.azurewebsites.net/.auth/me",
pStrXZumoAuth,
"user_id");
//pStrParam = user_id
}

Invalid Response Error when Retrieving Google Analytics Data with a Service Account in C# .NET

I'm trying to write an online application to access my google analytics data using a google service account. Here's my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace GA_server2server_POC.Models
{
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Analytics.v3;
using Google.Apis.Analytics.v3.Data;
using Google.Apis.Authentication.OAuth2;
using Google.Apis.Authentication.OAuth2.DotNetOpenAuth;
using Google.Apis.Util;
using Google.Apis.Services;
using Google.Apis.Requests;
public class Oauth_With_API
{
public static void ApiTest()
{
log4net.Config.XmlConfigurator.Configure();
const string ServiceAccountId = "xxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com";
const string ServiceAccountUser = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdeveloper.gserviceaccount.com";
AssertionFlowClient client = new AssertionFlowClient(
GoogleAuthenticationServer.Description, new X509Certificate2("C:\\Users\\rcarter\\Downloads\\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-privatekey.p12", "notasecret", X509KeyStorageFlags.Exportable))
{
Scope = "https://www.googleapis.com/auth/analytics.readonly",
ServiceAccountId = ServiceAccountUser
};
OAuth2Authenticator<AssertionFlowClient> authenticator = new OAuth2Authenticator<AssertionFlowClient>(client, AssertionFlowClient.GetState);
AnalyticsService service = new AnalyticsService(new BaseClientService.Initializer()
{
Authenticator = authenticator
});
string profileId = "ga:xxxxxxxx";
string startDate = "2013-07-01";
string endDate = "2013-07-15";
string metrics = "ga:visits";
DataResource.GaResource.GetRequest request = service.Data.Ga.Get(profileId, startDate, endDate, metrics);
request.Dimensions = "ga:date";
GaData data = request.Execute(); //error occurs here. After this, thread exits.
Console.WriteLine(data.TotalResults);
}
}
}
So far my code executes, but I get the following output:
WebDev.WebServer40.exe Information: 0 : DotNetOpenAuth, Version=4.0.0.11165, Culture=neutral, PublicKeyToken=2780ccd10d57b246 (official)
WebDev.WebServer40.exe Information: 0 : Preparing to send AssertionFlowMessage (2.0) message.
WebDev.WebServer40.exe Information: 0 : Sending AssertionFlowMessage request.
WebDev.WebServer40.exe Information: 0 : HTTP POST https://accounts.google.com/o/oauth2/token
WebDev.WebServer40.exe Information: 0 : The following required parameters were missing from the DotNetOpenAuth.OAuth2.Messages.AccessTokenFailedResponse message: {error,
}
WebDev.WebServer40.exe Information: 0 : Received UnauthorizedResponse response.
After this, the thread exits, and the program refuses to print any of the data. The trouble seems to occur at request.Execute();. The part that I find especially confusing, is that if I put a breakpoint on Console.WriteLine(data.TotalResults);, I can see the data I want in the local variable data. It contains everything I want to print, but I can't identify the cause of the error keeping it from doing anything after request.Execute();. After much searching, I haven't found much at all about the error listed above.
The code I'm using is based on the answer given to this question here. A few things have changed in the google analytics libraries since that question was answered, but much of my code is the same.
I've checked and re-checked all the account-specific variables. To test this, I'm running it on my local machine as a ASP.NET MVC 4 Web App.
Any help or advice on how to troubleshoot this issue is appreciated. Please let me know if I can provide more information which might help. Thanks for reading.
Try following one
using System.Security.Cryptography.X509Certificates;
using DotNetOpenAuth.OAuth2;
using Google.Apis.Analytics.v3;
using Google.Apis.Analytics.v3.Data;
using Google.Apis.Authentication.OAuth2;
using Google.Apis.Authentication.OAuth2.DotNetOpenAuth;
using Google.Apis.Services;
private void TestMethod()
{
try
{
string scope_url = "https://www.googleapis.com/auth/analytics.readonly";
//client_id: This is the "Email Address" one, not the "Client ID" one... oddly...
string client_id = "************-***********************#developer.gserviceaccount.com";
//key_file: This is the physical path to the key file you downloaded when you created your Service Account
string key_file = #"***************************************-privatekey.p12";
//key_pass: This is probably the password for all key files, but if you're given a different one, use that.
string key_pass = "notasecret";
AuthorizationServerDescription desc = GoogleAuthenticationServer.Description;
//key: Load up and decrypt the key
X509Certificate2 key = new X509Certificate2(key_file, key_pass, X509KeyStorageFlags.Exportable);
//client: we're using the AssertionFlowClient, because we're logging in with our certificate
AssertionFlowClient client = new AssertionFlowClient(desc, key) { ServiceAccountId = client_id, Scope = scope_url };
OAuth2Authenticator<AssertionFlowClient> auth = new OAuth2Authenticator<AssertionFlowClient>(client, AssertionFlowClient.GetState);
//gas: An instance of the AnalyticsService we can query
// AnalyticsService gas = null;// new AnalyticsService(auth);
var gas = new AnalyticsService(new BaseClientService.Initializer()
{
Authenticator = auth
});
//r: Creating our query
DataResource.GaResource.GetRequest r = gas.Data.Ga.Get("ga:*******", "2012-09-26", "2012-10-10", "ga:visitors");
//d: Execute and fetch the results of our query
GaData d = r.Fetch();
}
catch (Exception ex)
{
throw;
}
}

Categories