EOS-WebAPI/Common/FCMNotification.cs
Nidhi Bhargava d0ac8a7790 Code Commit
2025-09-04 17:30:22 +05:30

430 lines
16 KiB
C#

using LoggingHelper;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Script.Serialization;
namespace VECV_WebApi.Common
{
public class FCMNotification
{
#region Global Variables
/// <summary>
/// making object of LoggingUtility class available to this class
/// </summary>
LoggingUtility objLog = new LoggingUtility();
/// <summary>
/// making the data-log file path available to this class
/// </summary>
string path = HttpContext.Current.Server.MapPath(ConfigurationManager.AppSettings["PathLog"]);
/// <summary>
/// making error log file path available to this class
/// </summary>
string errorlogtf = (ConfigurationManager.AppSettings["ErrorLog"]);
#endregion
/// <summary>
/// Generates a Firebase Access Token manually using RSA256
/// </summary>
public string GetFirebaseAccessToken()
{
try
{
string serviceAccountPath = (ConfigurationManager.AppSettings["FCMJson"]);
var json = File.ReadAllText(serviceAccountPath);
var serviceAccountData = new JavaScriptSerializer().Deserialize<Dictionary<string, object>>(json);
string privateKey = serviceAccountData["private_key"].ToString().Replace("\\n", "\n");
string clientEmail = serviceAccountData["client_email"].ToString();
string tokenUri = serviceAccountData["token_uri"].ToString();
var now = DateTime.UtcNow;
long iat = (int)(now - new DateTime(1970, 1, 1)).TotalSeconds;
long exp = iat + 3600; // 1 hour expiration
var header = new { alg = "RS256", typ = "JWT" };
var payload = new
{
iss = clientEmail,
scope = "https://www.googleapis.com/auth/cloud-platform",
aud = tokenUri,
iat = iat,
exp = exp
};
string encodedHeader = Base64UrlEncode(new JavaScriptSerializer().Serialize(header));
string encodedPayload = Base64UrlEncode(new JavaScriptSerializer().Serialize(payload));
string unsignedToken = $"{encodedHeader}.{encodedPayload}";
string signedToken = SignWithRSA(unsignedToken, privateKey);
return signedToken;
}
catch (Exception ex)
{
Console.WriteLine("Error generating Firebase Access Token: " + ex.Message);
return string.Empty;
}
}
/// <summary>
/// Sends an FCM notification with error handling (For .NET 4.5)
/// </summary>
public bool SendFCMNotification(string fcmToken, string projectId, string accessToken,string ticketid)
{
string url = $"https://fcm.googleapis.com/v1/projects/{projectId}/messages:send";
objLog.ErrorLogFile("GlobalRepository GcmSendNotificationtest1", url, path, errorlogtf);
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/json";
request.Headers["Authorization"] = "Bearer " + accessToken;
objLog.ErrorLogFile("GlobalRepository GcmSendNotificationtest2", accessToken, path, errorlogtf);
var payload1 = new
{
message = new
{
token = fcmToken,
notification = new
{
title = "EOS Notification",
body = "EOS FCM!"
},
data = new
{
TicketId = ticketid,
NotificationCode = "null", // Change null to "null"
NotificationMessage = "2"
}
}
};
string jsonPayload = new JavaScriptSerializer().Serialize(payload1);
objLog.ErrorLogFile("GlobalRepository GcmSendNotification14", jsonPayload, path, errorlogtf);
try
{
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
streamWriter.Write(jsonPayload);
streamWriter.Flush();
streamWriter.Close();
}
var response = (HttpWebResponse)request.GetResponse();
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
string result = streamReader.ReadToEnd();
Console.WriteLine("FCM Response: " + result);
objLog.ErrorLogFile("GlobalRepository GcmSendNotification12", result, path, errorlogtf);
}
return true;
}
catch (WebException webEx)
{
using (var streamReader = new StreamReader(webEx.Response.GetResponseStream()))
{
string errorResponse = streamReader.ReadToEnd();
if (errorResponse.Contains("UNREGISTERED"))
{
objLog.ErrorLogFile("GlobalRepository GcmSendNotification9", "FCM token is unregistered. Removing from database...", path, errorlogtf);
// objModel.TicketId = model.TicketId;
// Console.WriteLine("FCM token is unregistered. Removing from database...");
// Remove token from DB
}
else
{
objLog.ErrorLogFile("GlobalRepository GcmSendNotification10", errorResponse, path, errorlogtf);
// Console.WriteLine("FCM Error: " + errorResponse);
}
}
}
catch (Exception ex)
{
objLog.ErrorLogFile("GlobalRepository GcmSendNotification11", ex.Message, path, errorlogtf);
// Console.WriteLine("Exception sending FCM: " + ex.Message);
}
return false;
}
public bool SendFCMNotificationWithMessage(string fcmToken, string projectId, string accessToken, string ticketid, string current_status, string previous_status, string show_message, string display_message,string message_tag,string notification_title)
{
string url = $"https://fcm.googleapis.com/v1/projects/{projectId}/messages:send";
objLog.ErrorLogFile("GlobalRepository GcmSendNotificationtest1", url, path, errorlogtf);
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/json";
request.Headers["Authorization"] = "Bearer " + accessToken;
string jsonPayload = "";
objLog.ErrorLogFile("GlobalRepository GcmSendNotificationtest2", accessToken, path, errorlogtf);
var payload1 = new
{
message = new
{
token = fcmToken,
data = new
{
TicketId = ticketid,
NotificationCode = "null", // Change null to "null"
NotificationMessage = "2",
CurrentStatus = current_status,
PreviosStatus = previous_status,
ShowMessage = show_message,
MessageTag = message_tag,
Notification_Title = notification_title,
DisplayMessge = display_message
}
}
};
jsonPayload = new JavaScriptSerializer().Serialize(payload1);
objLog.ErrorLogFile("GlobalRepository GcmSendNotification14", jsonPayload, path, errorlogtf);
try
{
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
streamWriter.Write(jsonPayload);
streamWriter.Flush();
streamWriter.Close();
}
var response = (HttpWebResponse)request.GetResponse();
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
string result = streamReader.ReadToEnd();
Console.WriteLine("FCM Response: " + result);
objLog.ErrorLogFile("GlobalRepository GcmSendNotification12", result, path, errorlogtf);
objLog.ErrorLogFile("GlobalRepository GcmSendNotification12", jsonPayload, path, errorlogtf);
}
return true;
}
catch (WebException webEx)
{
using (var streamReader = new StreamReader(webEx.Response.GetResponseStream()))
{
string errorResponse = streamReader.ReadToEnd();
if (errorResponse.Contains("UNREGISTERED"))
{
objLog.ErrorLogFile("GlobalRepository GcmSendNotification9", "FCM token is unregistered. Removing from database...", path, errorlogtf);
// objModel.TicketId = model.TicketId;
// Console.WriteLine("FCM token is unregistered. Removing from database...");
// Remove token from DB
}
else
{
objLog.ErrorLogFile("GlobalRepository GcmSendNotification10", errorResponse, path, errorlogtf);
// Console.WriteLine("FCM Error: " + errorResponse);
}
}
}
catch (Exception ex)
{
objLog.ErrorLogFile("GlobalRepository GcmSendNotification11", ex.Message, path, errorlogtf);
// Console.WriteLine("Exception sending FCM: " + ex.Message);
}
return false;
}
/// <summary>
/// Signs JWT using RSA with SHA256
/// </summary>
static string SignWithRSA(string data, string privateKeyPem)
{
byte[] keyBytes = Convert.FromBase64String(privateKeyPem.Replace("-----BEGIN PRIVATE KEY-----", "").Replace("-----END PRIVATE KEY-----", "").Replace("\n", ""));
// RSACryptoServiceProvider rsa = DecodeRSAPrivateKey(keyBytes);
RSACryptoServiceProvider rsa = DecodeRSAPrivateKey(privateKeyPem);
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
byte[] signedBytes = rsa.SignData(dataBytes, CryptoConfig.MapNameToOID("SHA256"));
return $"{data}.{Base64UrlEncode(signedBytes)}";
}
/// <summary>
/// Converts standard Base64 to Base64Url encoding
/// </summary>
static string Base64UrlEncode(object input)
{
string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(input.ToString()));
return base64.TrimEnd('=').Replace('+', '-').Replace('/', '_');
}
/// <summary>
/// Converts Base64 to Base64Url encoding
/// </summary>
static string Base64UrlEncode(byte[] input)
{
string base64 = Convert.ToBase64String(input);
return base64.TrimEnd('=').Replace('+', '-').Replace('/', '_');
}
/// <summary>
/// Decodes a RSA private key
/// </summary>
//static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privateKeyBytes)
//{
// RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
// rsa.DecodeRSAPrivateKey
// // rsa.ImportPkcs8PrivateKey(new ReadOnlySpan<byte>(privateKeyBytes), out _);
// return rsa;
//}
static RSACryptoServiceProvider DecodeRSAPrivateKey(string privateKeyPem)
{
try
{
// Remove headers and footers from PEM
string privateKeyBase64 = privateKeyPem
.Replace("-----BEGIN PRIVATE KEY-----", "")
.Replace("-----END PRIVATE KEY-----", "")
.Replace("\n", "")
.Replace("\r", "")
.Trim();
// Convert Base64 to byte array
byte[] privateKeyBytes = Convert.FromBase64String(privateKeyBase64);
return DecodeRSAPrivateKeyFromBytes(privateKeyBytes);
}
catch (Exception ex)
{
Console.WriteLine("Error decoding private key: " + ex.Message);
return null;
}
}
public async Task<string> SendFCMNotification2(string accesstoken)
{
string str = "success";
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://fcm.googleapis.com/v1/projects/eicher-eos-androidx/messages:send");
request.Headers.Add("Sender", "15420367036");
request.Headers.Add("Authorization", "Bearer " + accesstoken);
// request.Headers.Add("Authorization", "Bearer ya29.c.c0ASRK0GYrBkOReMU8gE-qqt9I3kd0Cop-00wEWRe1iBJBnQ71yWSujJ7S1QHsqngKLQk1ifiDslxNVwdEN8j6NOF9UmOhL9qnWcHpnXM4lXQOBJPyXF6-9eF-zRT1ZVA8Xcrb_EzY8BHsKokQoLcE5JVLDuOcZqqsAhdBZ3Wv5Z5WxweHtOHBhZtXkG3451K3vCLi5nhG-5RploO3PrGoXsMqmJKmXo3jBTO7u9YmMtnrKkK-Pd4cSPyn2pL6qIfJsqi3yn8tFX0X1OGR8gw8rp40AnX3ZL1NdtGoojOUmgBXnLn0Md81EiSdb7ejGfv4aagbIbfOFtcn98a6dls6IhvUWR2u0irEwl5UYQDLz1O-gdVQjaky5fAG384Kc92vQ7X1ct811pm6edjivJIak5Fofb5knYdbg9i9hiluf5teb0a20lsbnyJqQkbeWQjOk_SlhUiee-MeMzfZvcx4rSFOfrsj9RhfQ9voetzYM6znw1l1YBi5klIasd3V7labyrd6kqdcF_ZFqMyse5ButW50R14gZ9UJFgbg2nmYiilx833hkkv5elxX0JSO9rvzaawncZp5apW4Mpp7-6UJ0b6bid8RvZtXsBnMixwOmUiBfSVp2lROBtWu9nrRrvh_QVb5dBllrgbBb1bgwScSo4XcgnY3mcdwYFVB_lk10exd-4RJlqj9kf5QJF3hRY3IjOW02kJiZq2WeQ6w6gu5X3sXRo8cul8surm0i5XMzqB5wqgafeexVda1Osz7t6kvXj7mgnk57Jmxjc823eknaM9Fn-cax-IuR8sze9f2c4rSV2h8sbwbcI3cvinp6ocBnhIoSvFBBjFhhXs5wBVMY_rRZbQijjj8-016JkbIb9ztWYruynx5g2oq3IhWpje_xfnMIjughaboVgmi9c-nvcdQSMygxOOI4Zdmi2YOomv0b2WjvW-wBwtoeJ2gerY3o9xmdX-1wcrdyXsISidezSoz_eWIIVx12ikmzcJ0X5rSoJMhBYti0--");
var content = new StringContent("{\r\n \"message\": {\r\n \"token\":\"eZM2vdFeaTA:APA91bG56oeC0Sv6TeBj1Sf4v4KfBEVpRkGL24yLgXNKU5bKOwZboQiHRbtnWMxzl1yt7bf2zrzyEJNI6HJwDY2iXmwZSJTF0RpRAqTb4pcpOHr7AZA0Gwk\",\r\n \"data\": {\"TicketId\":\"TICKETID-278411\",\"NotificationCode\":null,\"NotificationMessage\":\"2\"}\r\n }\r\n}\r\n\r\n", null, "application/json");
request.Content = content;
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
return str;
}
/// <summary>
/// Decodes an RSA private key from byte array (Compatible with .NET 4.5)
/// </summary>
static RSACryptoServiceProvider DecodeRSAPrivateKeyFromBytes(byte[] privateKeyBytes)
{
try
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
using (MemoryStream ms = new MemoryStream(privateKeyBytes))
using (BinaryReader br = new BinaryReader(ms))
{
byte[] seqOid = { 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00 };
if (br.ReadByte() != 0x30)
return null;
ReadASNLength(br);
if (br.ReadByte() != 0x02)
return null;
int version = br.ReadByte();
if (version != 0x00)
return null;
if (!CompareByteArrays(br.ReadBytes(15), seqOid))
return null;
if (br.ReadByte() != 0x04)
return null;
ReadASNLength(br);
if (br.ReadByte() != 0x30)
return null;
ReadASNLength(br);
RSAParameters rsaParams = new RSAParameters
{
Modulus = ReadASNInteger(br),
Exponent = ReadASNInteger(br),
D = ReadASNInteger(br),
P = ReadASNInteger(br),
Q = ReadASNInteger(br),
DP = ReadASNInteger(br),
DQ = ReadASNInteger(br),
InverseQ = ReadASNInteger(br)
};
rsa.ImportParameters(rsaParams);
return rsa;
}
}
catch (Exception ex)
{
Console.WriteLine("Error decoding RSA private key from bytes: " + ex.Message);
return null;
}
}
/// <summary>
/// Reads an ASN.1 length
/// </summary>
static int ReadASNLength(BinaryReader br)
{
int length = br.ReadByte();
if ((length & 0x80) == 0)
return length;
int bytesToRead = length & 0x7F;
return br.ReadByte();
}
/// <summary>
/// Reads an ASN.1 Integer
/// </summary>
static byte[] ReadASNInteger(BinaryReader br)
{
if (br.ReadByte() != 0x02)
return null;
int length = ReadASNLength(br);
return br.ReadBytes(length);
}
/// <summary>
/// Compares two byte arrays
/// </summary>
static bool CompareByteArrays(byte[] a, byte[] b)
{
if (a.Length != b.Length)
return false;
for (int i = 0; i < a.Length; i++)
{
if (a[i] != b[i])
return false;
}
return true;
}
}
}