Duplicate registrations in Azure Notification Hub with Xamarin Android - azure

I am using Azure Notification Hub with Xamarin Android. It works fine in normal scenario and I am able to get push notifications on my registered tags but on update of tag or reregistering the hub it creates duplicate registrations. Also the tags which were removed post registration still gets the notification. Below is the sample snippet for the same
try
{
Hub.UnregisterAll(registrationId);
}
catch (Exception ex)
{
}
var tags = getting active tags
try
{
var hubregistration = Hub.Register(registrationId, tags);
}
catch (Exception ex)
{
}

AFAIK, the Registration Token (registrationId) issued by GCM is used to identity the client app, and it may be the same when re-register from GCM without unregistering from GCM. Based on your code, you are using the Registrations model. Hub.UnregisterAll(registrationId) would try to un-register the registrations with the same Registration Token (pnsHandle) from your azure notification hub.
I would recommend you capturing the exception when you call UnregisterAll. Also, you could leverage Server Explorer from Visual Studio, choose your notification hub, then view and manage all the registrations in your hub as follows to narrow this issue:
Note: You could check with your device registrations and try to find whether you could retrieve the duplicated registrations (same PNS Identifier (Registration Token), different tags / Azure Registration ID or different PNS Identifier (Registration Token) for the same client app, etc.).
If you find different PNS Identifier (Registration Token) for the same client app, I assume that your client app need to store the previous Registration Token and compare with the latest Registration Token, UnregisterAll the old Registration Token if not match firstly, then register the new Registration Token with your notification hub.
Additionally, the Installations model could avoid the duplicate registrations. For more details, you could refer to Registration management.

This is my working methods for Register and UnRegister from azure hub
void unregister ()
{
try {
NotificationHub hub = new NotificationHub (Constants.NotificationHubName, Constants.ListenConnectionString, this);
hub.UnregisterAll (FirebaseInstanceId.Instance.Token);
} catch (Exception ex) {
}
}
void register ()
{
try {
NotificationHub hub = new NotificationHub (Constants.NotificationHubName, Constants.ListenConnectionString, this);
var tags = new List<string> () { ... };
hub.Register (FirebaseInstanceId.Instance.Token, tags.ToArray ());
} catch (Exception ex) {
}
}
based on this documentation https://learn.microsoft.com/en-us/azure/notification-hubs/xamarin-notification-hubs-push-notifications-android-gcm

Related

Communicate to an Azure service bus from a different subscription

I created a service bus for a learning purpose, and I want to pass messages to it. I went through the official Microsoft documentation which teaches to use the DefaultAzureCredential so that the stored credentials in Visual Studio are gonna get used.
The problem is, I have logged in to Visual studio from my work email, which I use daily. And the new service bus is in a different subscription, and it's attached to my personal email.
So, is there a way to get the service bus to work, without logging in with my personal email, and CLI configurations? Any alternatives for this DefaultAzureCredential object?
Sample code (From Microsoft documentation)
ServiceBusClient client;
const int numOfMessages = 3;
var clientOptions = new ServiceBusClientOptions
{
TransportType = ServiceBusTransportType.AmqpWebSockets
};
client = new ServiceBusClient("asb-test.servicebus.windows.net",
new DefaultAzureCredential(),
clientOptions);
sender = client.CreateSender("email");
using ServiceBusMessageBatch messageBatch = await
sender.CreateMessageBatchAsync();
var result = messageBatch.TryAddMessage(new ServiceBusMessage($"Message"));
if (!result)
{
throw new Exception($"The message is too large to fit in the batch.");
}
try
{
await sender.SendMessagesAsync(messageBatch);
Console.WriteLine($"A batch of {numOfMessages} messages has been published to the queue.");
}
finally
{
await sender.DisposeAsync();
await client.DisposeAsync();
}
If the subscriptions are in the same tenant, this is not a problem, you can still easily assign permissions. But I guess what you mean to say is that the subs are in two different tenants.
You could try to add your work account as a guest user in your personal tenant, then you should also be able to assign it permissions.

How to send push notifications in Flutter android app?

I developed a flutter android app. and my database is MongoDB. I use Node.js API to connect my flutter app with the MongoDB. I want to send push notifications when a new data record is coming to MongoDB. How can I do that?
The simplest way is to use Firebase Cloud Messaging. Especially since Google is deprecating GCM which was previously used for Android. Also Firebase cloud messaging is free and can be used for both iOS and Android. Apple's APN service will require a setup as well though and a paid developer account.
Create a Firebase project if you haven't already and enable cloud messaging.
To set up your Node.js server so that it can send push notifications to your android and IOS devices. Click on the Project Overview, Settings and service accounts and follow the directions to generate a private key for your project and follow the instructions for setup. Also npm install "firebase-admin".
Once you have firebase setup refer to these docs for how to send messages. https://firebase.google.com/docs/cloud-messaging/send-message
There are several ways to send messages. You can send messages directly.
with this code
// This registration token comes from the client FCM SDKs.
var registrationToken = 'YOUR_REGISTRATION_TOKEN';
var message = {
data: {
score: '850',
time: '2:45'
},
token: registrationToken
};
// Send a message to the device corresponding to the provided
// registration token.
admin.messaging().send(message)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
});
You can also create topics for devices to subscribe to as well if you are sending out mass notifications. More examples once again are within the docs. Now if you are wondering what the token is that is the next step.
The token comes from the unique device that is connecting to your platform. You can get the token by installing the Firebase Messaging sdk from https://pub.dev/packages/firebase_messaging and following the instructions to add the necessary dependencies to your pubsec.yaml and properly configure your Android manifest and iOS files for the changes.
This package will give you methods to grab your communicate and to receive the notifications that you have sent form your Node.JS server.
Here is an example of grabbing the token from your device on the frontend in flutter.
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
bool _initialized = false;
Future<void> init() async {
if (!_initialized) {
// For iOS request permission first.
_firebaseMessaging.requestNotificationPermissions();
_firebaseMessaging.configure(onMessage: (Map<String, dynamic> `enter code here`message) {
print('onMessage: $message');
Platform.isAndroid
? showNotification(message['notification'])
: showNotification(message['aps']['alert']);
return;
}, onResume: (Map<String, dynamic> message) {
print('onResume: $message');
return;
}, onLaunch: (Map<String, dynamic> message) {
print('onLaunch: $message');
return;
});
// For testing purposes print the Firebase Messaging token
String token = await _firebaseMessaging.getToken();
print("FirebaseMessaging token: $token");
_initialized = true;
}
}
At this point you would most likely save the token to your MongoDB database and associate the token with your user and that specific device. Of course you would have to also install the firebase core and for Flutter as well and do all of the necessary configurations.
You are still able to maintain your NodeJS API and MongoDB database and use free cloud messaging service to push your notifications for your server to your device.

getting an error trying to register device with Azure Notification Hub

I have a xamarin app That will send notifications via Azure functions and its notification hub out put binding.
In xamarin app i have an android service that gets the token and stores in cache
[Service]
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
public class MyFirebaseIIDService : FirebaseInstanceIdService
{
private App _app => (App)Xamarin.Forms.Application.Current;
private readonly ICachingService _cachingService;
public MyFirebaseIIDService()
{
var xx = typeof(ICachingService);
_cachingService = (CachingService)App.Instance.Container.Resolve(xx);
}
public override void OnTokenRefresh()
{
var refreshedToken = FirebaseInstanceId.Instance.Token;
_cachingService.FireBaseToken = refreshedToken;
Console.WriteLine($"Token received: {refreshedToken}");
// SendRegistrationToServerAsync(refreshedToken);
}
}
after the user is logged in i want to use its Id as tag and i call another android service that tries to register a device with this tag
var client = new MobileServiceClient(App.MobileServiceUrl);
var push = client.GetPush();
var reg = new Registration(token, tags);
await push.RegisterAsync(reg);
at this point i have token and tags list that contains userId because later i want that this user only gets notification meant for them only.
on the push.RegisterAsyn method i get an error.
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
please note that the App.MobileServiceUrl is the url of Azure Mobile App that is connected to notification hub
Other than that this Azure Mobile App have just the default TODO controllers and everything that comes with the template. My sending notification code will be in azure functions using notification hub output binding.
I also updated all azure related nugets no diffrence
it tries to hit this url for registration.
https://xxxx.azurewebsites.net/push/registrations?deviceId=dTd4wba1KTU:APA91bHKOZRX9LFzEGD-yqyz4p-whqh6UsoEAlgpFHfBxu00MhLo-------yyyyyyyyyyyyyyyyeuoRmH4h9czeQbvGRgbwt4zMlrvRIlvLDZ-kTu_Dcu2iHx9I5u0gheQQ3Z2tYq66O&platform=gcm
i was using a wrong nuget. i had to use
Microsoft.Azure.Mobile.Client
instead i was using Azure Mobile Services nuget.
both have MobileServiceClient class thats why i got confused. now i dont get the exception.

Azure AD Claims in IdentityServer4

I have taken this sample from github to attempt to use IdentityServer4 and Azure AD for authentication.
While I have it working and returning a token, it seems that the claims that I would have expected to receive from Azure AD are not included in the token(s) issued through IdentityServer.
It could be that this is intentional and that I have misunderstood this flow, but I was hoping that the roles that a user is assigned through Azure AD (plus the tenant ID and other useful 'bits' from the Azure token) would be able to be included in the tokens issued to the client.
Would anyone be able to shed some light on this for me? I can paste code in here but the link to the github code is pretty much the same as what I am using.
I was trying to do the same thing, and managed to eventually piece bits together from looking at the IS4 docs, Github and StackOverflow.
You need to configure a new instance of IProfileService (Docs) in order to tell IdentityServer4 which additional claims for a user's identity (obtained from Azure AD in your case) you want to be passed back to the client.
An example might look like this:
public class CustomProfileService : IProfileService
{
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
// Get the 'upn' claim out from the identity token returned from Azure AD.
var upnClaim = context.Subject.FindFirst(c => c.Type == ClaimTypes.Upn);
// Get 'firstname' and 'givenname' claims from the identity token returned from Azure AD.
var givenNameClaim = context.Subject.FindFirst(c => c.Type == ClaimTypes.GivenName);
var surNameClaim = context.Subject.FindFirst(c => c.Type == ClaimTypes.Surname);
// Add the retrieved claims into the token sent from IdentityServer to the client.
context.IssuedClaims.Add(upnClaim);
context.IssuedClaims.Add(givenNameClaim);
context.IssuedClaims.Add(surNameClaim);
}
public Task IsActiveAsync(IsActiveContext context)
{
context.IsActive = true;
return Task.CompletedTask;
}
}
You will then need to register this service in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer()
.AddDeveloperSigningCredential()
// Register the new profile service.
.AddProfileService<CustomProfileService>();
}
Finally, inside of your AccountController.cs (within the IdentityServer4 project - I'm assuming you already have this, see here for a starter setup if not), you need to add the following to ExternalLoginCallback():
[HttpGet]
public async Task<IActionResult> ExternalLoginCallback()
{
//...
// this allows us to collect any additonal claims or properties
// for the specific protocols used and store them in the local auth cookie.
// this is typically used to store data needed for signout from those protocols.
var additionalLocalClaims = new List<Claim>();
// ADD THIS LINE TO TELL IS4 TO ADD IN THE CLAIMS FROM AZURE AD OR ANOTHER EXTERNAL IDP.
additionalLocalClaims.AddRange(claims);
//...
}
Hope this helps.

Send push notification to all registered devices with Azure Notification Hub in .NET

I'm working with Azure Notification Hub, and I want to send a push notification message to all registered device in .NET backend. But I'm not sure this way will send to all devices because I don't have the way to check the number of devices received push message.
So, how to, I can send a push message to all devices or can make sure this way is correct?
public static async Task<bool> SendBroadcast(string msg)
{
try
{
var notificationHubClient = NotificationHubClient.CreateClientFromConnectionString(ConfigurationManager.AppSettings["ServiceBusPushNotificationConnectionString"], ConfigurationManager.AppSettings["ServiceBusPushNotificationName"]);
Dictionary<string, string> param = new Dictionary<string, string>();
param.Add("message", msg);
param.Add("alert", msg);
var template = new TemplateNotification(param);
var result = await notificationHubClient.SendNotificationAsync(template);
Console.WriteLine(JsonConvert.SerializeObject(result));
return true;
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
return false;
}
}
If you don't specify any tag expression, that means it's broadcast. All devices will receive the notification. You can track how many devices are received through using Per Message Telemetry. Please see below links for same.
https://msdn.microsoft.com/en-us/library/azure/mt608135.aspx
https://azure.microsoft.com/en-us/blog/push-notification-hub-telemetry-expiry-update/
You need to use tags as described in Routing and Tag Expressions:
The only way to target specific registrations is to associate them
with a tag, then target that tag. As discussed in Registration
Management, in order to receive push notifications an app has to
register a device handle on a notification hub. Once a registration is
created on a notification hub, the application backend can send push
notifications to it. The application backend can choose the
registrations to target with a specific notification in the following
ways:
Broadcast: all registrations in the notification hub receive the
notification.
Tag: all registrations that contain the specified tag receive the
notification.
Tag expression: all registrations whose set of tags match the
specified expression receive the notification.
Note, there're limitations on broadcast messages that you need to take into account.
Take a look at the Breaking News App Sample on details about how to use broadcast notifications.

Resources