I'm trying to send multiple web notifications using topic instead of looping send notification to each token.
But I stuck at sending notification to topic using Credential with 401 unauthorized
Here is my code
Firstly, I request web browser permission for notification token via this Firebase sample code: https://github.com/firebase/quickstart-js/blob/master/messaging
On index.html
I change code like this to send token to server for subscribing to topic
function resetUI() {
clearMessages();
// [START get_token]
// Get Instance ID token. Initially this makes a network call, once retrieved
// subsequent calls to getToken will return from cache.
messaging.getToken().then(function (currentToken) {
if (currentToken) {
console.log("Get Token OK: " + currentToken);
sendTokenToServer(currentToken);
} else {
// Show permission request.
console.log('No Instance ID token available. Request permission to generate one.');
// Reset Token on Server to False
setTokenSentToServer(false);
// Request permission for Token
requestPermission();
}
}).catch(function (err) {
console.log('An error occurred while retrieving token. ', err);
setTokenSentToServer(false);
});
// [END get_token]
}
// Send the Instance ID token your application server, so that it can:
// - send messages back to this app
// - subscribe/unsubscribe the token from topics
function sendTokenToServer(currentToken) {
if (!(window.localStorage.getItem('EDXNotificationIsSentToServer') === '1')) {
console.log('Sending token to server...');
var tokenObject = {
token: currentToken
};
$.ajax({
type: "POST",
url: '@Url.Action("SaveToken", "Api")',
data: JSON.stringify(tokenObject),
contentType: "application/json; charset=utf-8",
success: function (data) {
if (data) {
// TODO(developer): Send the current token to your server.
setTokenSentToServer(true);
}
},
complete: function (status) {
},
error: function (error) { }
});
} else {
console.log('Token already sent to server so won\'t send it again unless it changes');
}
}
On server, I stored token to database and subscribe it to test topic
public class ApiController : Controller
{
private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// Lưu token
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
public JsonResult SaveToken(TBL_Customer_Tokens model)
{
try
{
if (!string.IsNullOrEmpty(model.token))
{
bool ret = true;
if(GCMUtils.SubcribeTokenToTopic(model.token, ConfigurationManager.AppSettings["GCMTopic"]))
ret = DataServiceFactory.GetCustomerService().SaveToken(model.token); // Save token to database, don't care about this function. It is ok
if (ret)
Json(true, JsonRequestBehavior.AllowGet);
}
}
catch (Exception ex)
{
log.Debug(ex.Message, ex);
}
return Json(false, JsonRequestBehavior.AllowGet);
}
}
Here is GCMUtils
public class GCMUtils
{
private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public static bool SubcribeTokenToTopic(string token, string topic)
{
try
{
string url = String.Format("https://iid.googleapis.com/iid/v1/{0}/rel/topics/{1}", token, topic);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.ContentType = "application/json";
req.Headers.Add("Authorization", "key=" + ConfigurationManager.AppSettings["GCMServerKey"]);
req.Method = "POST";
req.ContentLength = 0;
Stream reqStream = req.GetRequestStream();
reqStream.Close();
HttpWebResponse response = (HttpWebResponse)req.GetResponse();
if (response.StatusCode == HttpStatusCode.OK)
return true;
}
catch (Exception ex)
{
log.Debug(ex.Message, ex);
}
return false;
}
}
Token is retrieved, saved to database and successfully subscribed to topic. So you may ignore above code, analyze only problem belows
Secondly, I'm trying to send web notification to Topic instead of sending single notification to each token https://firebase.google.com/docs/cloud-messaging/js/topic-messaging
public static bool SendFirebaseTopicNotificationAsync(string topicSubscription, NotificationPayload dat)
{
try
{
List<string> scopes = new List<string>();
scopes.Add("https://www.googleapis.com/auth/firebase");
scopes.Add("https://www.googleapis.com/auth/firebase.messaging");
string accesstoken = GoogleCredential.FromFile(ConfigurationManager.AppSettings["GoogleCredentialFilePath"])
.CreateScoped(scopes)
.UnderlyingCredential
.GetAccessTokenForRequestAsync().Result;
WebRequest tRequest = WebRequest.Create("https://fcm.googleapis.com/v1/projects/test-project/messages:send");
tRequest.Method = "post";
tRequest.Headers.Add("Authorization", "bearer " + accesstoken);
tRequest.ContentType = "application/json";
var payload = new
{
message = new
{
topic = topicSubscription,
notification = new {
title = dat.title,
body = dat.body
},
webpush = new
{
fcm_options = new {
link = dat.click_action
}
}
}
};
string postbody = JsonConvert.SerializeObject(payload).ToString();
Byte[] byteArray = Encoding.UTF8.GetBytes(postbody);
tRequest.ContentLength = byteArray.Length;
using (Stream dataStream = tRequest.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
using (WebResponse tResponse = tRequest.GetResponse())
{
using (Stream dataStreamResponse = tResponse.GetResponseStream())
{
if (dataStreamResponse != null) using (StreamReader tReader = new StreamReader(dataStreamResponse))
{
String sResponseFromServer = tReader.ReadToEnd();
Console.WriteLine(sResponseFromServer);
return true;
}
}
}
}
}
catch (Exception ex)
{
log.Debug(ex.Message, ex);
}
return false;
}
Note that test-project
in SendFirebaseTopicNotificationAsync
is project name. I'm sure it is true
ConfigurationManager.AppSettings["GoogleCredentialFilePath"]
is absolute file path to .json key file which I'm following this guideline to retrieve https://www.c-sharpcorner.com/article/retrieve-access-token-for-google-service-account-form-json-or-p12-key-in-c-sharp/
But I get 401 Unauthorized
error. It seems access token from GoogleCredential
is wrong
Any helps will be appreciated
Thank you