public async Task <IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return(Page()); } _context.Items.Add(Item); await _context.SaveChangesAsync(); //use your own private and public key: //generator here: https://web-push-codelab.glitch.me/ var vapidDetails = new VapidDetails(@"<youremail>", "<publickey>", "<privatekey>"); var pushSubscriptions = await _context.PushSubscriptions.ToListAsync(); var webPushClient = new WebPushClient(); var tasks = new List <Task>(); foreach (var pushSubscription in pushSubscriptions) { var webPushSubscription = new WebPush.PushSubscription(pushSubscription.EndPoint, pushSubscription.P256dh, pushSubscription.Auth); tasks.Add(webPushClient.SendNotificationAsync(webPushSubscription, "test", vapidDetails)); } await Task.WhenAll(tasks); return(RedirectToPage("./Index")); }
public async Task SendNotificationAsync(PushSubscriptionUser subscription, WebPushNotificationMessages message, CancellationToken cancellationToken) { var vapidDetails = new VapidDetails(this._options.Subject, this._options.PublicKey, this._options.PrivateKey); PushSubscription pushSubscription = new WebPush.PushSubscription(subscription.Endpoint, subscription.P256DH, subscription.Auth); //var payload = System.Text.Json.JsonSerializer.Serialize(message); var settings = new JsonSerializerSettings(); settings.ContractResolver = new DefaultContractResolver { NamingStrategy = new JsonLowerCaseNamingStrategy() };; var payload = JsonConvert.SerializeObject(message, settings); try { await _pushClient.SendNotificationAsync(pushSubscription, payload, vapidDetails); } catch (WebPushException ex) { { using (IStoreRepositoryAccessor pushstoreAccessor = _pushStoreAccessorProvider.GetStoreRepositoryAccessor()) { subscription.DateEndTime = DateTime.Now; await pushstoreAccessor.StoreRepository.UpdateSubscriptionAsync(subscription); } _logger?.LogInformation("Subscription has expired or is no longer valid and has been removed."); } Console.Error.WriteLine("(WebPush) Error sending push notification: " + ex.Message); } }
public async Task SendNotificationAsync(WP.PushSubscription subscription, PushMessage message, string target) { _logger.LogInformation($"Sending VAPID push: {message.Content}: Image {_options.ImageUrl}"); var sub = new WebPush.PushSubscription( subscription.Endpoint, subscription.Keys["p256dh"], subscription.Keys["auth"] ); var vapid = new VapidDetails(_options.Subject, _options.PublicKey, _options.PrivateKey); var payload = JsonConvert.SerializeObject(new { notification = new { title = message.Topic, body = message.Content, icon = _options.ImageUrl, click_action = string.IsNullOrEmpty(target) ? _options.ClickUrl : target } }); var client = new WebPushClient(); try { _logger.LogDebug($"VAPID: Push to {subscription.Endpoint}"); await client.SendNotificationAsync(sub, payload, vapid); } catch (WebPushException ex) { _logger.LogError($"ERROR in VAPID: {ex.Message}"); _logger.LogError($"{subscription.Endpoint}"); } catch (Exception ex) { _logger.LogError($"ERROR in VAPID: {ex.Message}"); _logger.LogError($"{subscription.Endpoint}"); } }
public void SendNotification(Subscription subscription, Notification payload) { var webPushSubscription = new WebPush.PushSubscription( subscription.EndPoint, subscription.Code, subscription.Auth); _pushClient.SendNotification(webPushSubscription, Newtonsoft.Json.JsonConvert.SerializeObject((WebPushAdapter)payload)); }
public async Task RunAsync(CancellationToken cancellationToken = default) { var subscriptions = await _dbContext.PushSubscriptions .ToArrayAsync(cancellationToken) .ConfigureAwait(false); var teams = await _dbContext.Teams .ToArrayAsync(cancellationToken) .ConfigureAwait(false); var webPushClient = new WebPushClient(); foreach (var sub in subscriptions) { if (string.IsNullOrWhiteSpace(sub.Data)) { continue; } dynamic jsonObj = JsonConvert.DeserializeObject(sub.Data); string pushEndpoint = (string)jsonObj.endpoint; string p256Dh = (string)jsonObj.keys?.p256dh; string auth = (string)jsonObj.keys?.auth; if (string.IsNullOrWhiteSpace(auth)) { continue; } string payload = CreatePayload(teams); try { var subscription = new WebPush.PushSubscription(pushEndpoint, p256Dh, auth); await webPushClient.SendNotificationAsync(subscription, payload, _vapidDetails); } catch (WebPushException e) when(e.Message.Contains("no longer valid", StringComparison.OrdinalIgnoreCase)) { _logger.LogWarning(e, "Remvoing invalid subscription."); await RemoveSubscription(sub, cancellationToken) .ConfigureAwait(false); } catch (WebPushException e) { _logger.LogError(e, "Unable to push notfications"); } } }
public void SendNotification(string payload) { var subscription = this.context.PushSubscription.FirstOrDefault(x => x.Id == 2); var endpoint = subscription.Endpoint; var p256dh = subscription.P256; var auth = subscription.Auth; //VapidDetails vapidDetails = VapidHelper.GenerateVapidKeys(); var subject = @"mailto:[email protected]"; var publicKey = "BME6-YvoCvVwtas9T87mOfqI5ZfuQJiQRq-GaHWUWk3T7xW36gFUNrltXDgRZReOaSwEXq__EIuhH8DU7eQ9ITI"; var privateKey = "4GCQq4G5QY8eQyPdngx18hfzSMrEuwmURBX1csaybPk"; var subs = new WebPush.PushSubscription(endpoint, p256dh, auth); var vapid = new VapidDetails(subject, publicKey, privateKey); pushClient.SendNotification(subs, payload, vapid); }
public async Task <IActionResult> Post([FromBody] JObject body) { dynamic jsonObj = body; string pushEndpoint = (string)jsonObj.endpoint; string p256dh = (string)jsonObj.keys?.p256dh; string auth = (string)jsonObj.keys?.auth; string payload = JsonConvert.SerializeObject(new PushNotification { Title = "Nice! You are subscribed to push notifications.", Options = new PushNotificationOptions { Icon = "favicon.ico", } }); string json = body.ToString(); await _dbContext.PushSubscriptions.AddAsync(new Data.PushSubscription { Data = json, }); await _dbContext.SaveChangesAsync(); var webPushClient = new WebPushClient(); try { var subscription = new PushSubscription(pushEndpoint, p256dh, auth); await webPushClient.SendNotificationAsync(subscription, payload, _vapidDetails); } catch (WebPushException exception) { Console.WriteLine("Http STATUS code" + exception.StatusCode); } return(Ok()); }
public void Broadcast(string message) { var devices = _context.Devices.ToList(); foreach (var device in devices) { try { string vapidPublicKey = _configuration.GetSection("VapidKeys")["PublicKey"]; string vapidPrivateKey = _configuration.GetSection("VapidKeys")["PrivateKey"]; var pushSubscription = new PushSubscription(device.PushEndpoint, device.PushP256DH, device.PushAuth); var vapidDetails = new VapidDetails("mailto:[email protected]", vapidPublicKey, vapidPrivateKey); _webPushClient.SendNotification(pushSubscription, message, vapidDetails); } catch (Exception ex) { _logger.LogError(ex.StackTrace, ex); } } }
/// <summary> /// Handle Web Push responses. /// </summary> /// <param name="response"></param> /// <param name="subscription"></param> private static void HandleResponse(HttpResponseMessage response, PushSubscription subscription) { // Successful if (response.IsSuccessStatusCode) { return; } // Error var responseCodeMessage = @"Received unexpected response code: " + (int)response.StatusCode; switch (response.StatusCode) { case HttpStatusCode.BadRequest: responseCodeMessage = "Bad Request"; break; case HttpStatusCode.RequestEntityTooLarge: responseCodeMessage = "Payload too large"; break; case (HttpStatusCode)429: responseCodeMessage = "Too many request"; break; case HttpStatusCode.NotFound: case HttpStatusCode.Gone: responseCodeMessage = "Subscription no longer valid"; break; } var details = response.Content?.ReadAsStringAsync().GetAwaiter().GetResult(); var message = string.IsNullOrEmpty(details) ? responseCodeMessage : $"{responseCodeMessage}. Details: {details}"; throw new WebPushException(message, subscription, response); }
public async Task SendNotificationAsync(Lib.Net.Http.WebPush.PushSubscription subscription, PushMessage message, CancellationToken cancellationToken) { try { WebPush.PushSubscription sub = new WebPush.PushSubscription(); foreach (KeyValuePair <string, string> en in subscription.Keys) { Console.WriteLine("Keys and Values: "); Console.WriteLine(en.Key + " : " + en.Value); } sub.Auth = subscription.Keys["auth"]; sub.Endpoint = subscription.Endpoint; sub.P256DH = subscription.Keys["p256dh"]; await _webPushClient.SendNotificationAsync(sub, message.Content, vapidDetails); } catch (Exception ex) { await HandlePushMessageDeliveryExceptionAsync(ex, subscription); } }
/// <summary> /// To get a request without sending a push notification call this method. /// /// This method will throw an ArgumentException if there is an issue with the input. /// </summary> /// <param name="subscription">The PushSubscription you wish to send the notification to.</param> /// <param name="payload">The payload you wish to send to the user</param> /// <param name="options">Options for the GCM API key and vapid keys can be passed in if they are unique for each notification.</param> /// <returns>A HttpRequestMessage object that can be sent.</returns> public HttpRequestMessage GenerateRequestDetails(PushSubscription subscription, string payload, Dictionary <string, object> options = null) { if (!Uri.IsWellFormedUriString(subscription.Endpoint, UriKind.Absolute)) { throw new ArgumentException(@"You must pass in a subscription with at least a valid endpoint"); } HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, subscription.Endpoint); if (!String.IsNullOrEmpty(payload) && (String.IsNullOrEmpty(subscription.Auth) || String.IsNullOrEmpty(subscription.P256DH))) { throw new ArgumentException(@"To send a message with a payload, the subscription must have 'auth' and 'p256dh' keys."); } string currentGCMAPiKey = _gcmAPIKey; VapidDetails currentVapidDetails = _vapidDetails; int timeToLive = DefaultTtl; Dictionary <string, object> extraHeaders = new Dictionary <string, object>(); if (options != null) { List <string> validOptionsKeys = new List <string> { "headers", "gcmAPIKey", "vapidDetails", "TTL" }; foreach (string key in options.Keys) { if (!validOptionsKeys.Contains(key)) { throw new ArgumentException(key + " is an invalid options. The valid options are" + String.Join(",", validOptionsKeys)); } } if (options.ContainsKey("headers")) { Dictionary <string, object> headers = options["headers"] as Dictionary <string, object>; if (headers == null) { throw new ArgumentException("options.headers must be of type Dictionary<string,object>"); } extraHeaders = headers; } if (options.ContainsKey("gcmAPIKey")) { string gcmAPIKey = options["gcmAPIKey"] as string; if (gcmAPIKey == null) { throw new ArgumentException("options.gcmAPIKey must be of type string"); } currentGCMAPiKey = gcmAPIKey; } if (options.ContainsKey("vapidDetails")) { VapidDetails vapidDetails = options["vapidDetails"] as VapidDetails; if (vapidDetails == null) { throw new ArgumentException("options.vapidDetails must be of type VapidDetails"); } currentVapidDetails = vapidDetails; } if (options.ContainsKey("TTL")) { int?ttl = options["TTL"] as int?; if (ttl == null) { throw new ArgumentException("options.TTL must be of type int"); } //at this stage ttl cannot be null. timeToLive = (int)ttl; } } string cryptoKeyHeader = null; request.Headers.Add("TTL", timeToLive.ToString()); foreach (KeyValuePair <string, object> header in extraHeaders) { request.Headers.Add(header.Key, header.Value.ToString()); } if (!String.IsNullOrEmpty(payload)) { if (String.IsNullOrEmpty(subscription.P256DH) || String.IsNullOrEmpty(subscription.Auth)) { throw new ArgumentException(@"Unable to send a message with payload to this subscription since it doesn't have the required encryption key"); } EncryptionResult encryptedPayload = Encryptor.Encrypt(subscription.P256DH, subscription.Auth, payload); request.Content = new ByteArrayContent(encryptedPayload.Payload); request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); request.Content.Headers.ContentLength = encryptedPayload.Payload.Length; request.Content.Headers.ContentEncoding.Add("aesgcm"); request.Headers.Add("Encryption", "salt=" + encryptedPayload.Base64EncodeSalt()); cryptoKeyHeader = @"dh=" + encryptedPayload.Base64EncodePublicKey(); } else { request.Content = new ByteArrayContent(new byte[0]); request.Content.Headers.ContentLength = 0; } bool isGCM = subscription.Endpoint.StartsWith(@"https://android.googleapis.com/gcm/send"); if (isGCM) { if (!String.IsNullOrEmpty(currentGCMAPiKey)) { request.Headers.TryAddWithoutValidation("Authorization", "key=" + currentGCMAPiKey); } } else if (currentVapidDetails != null) { Uri uri = new Uri(subscription.Endpoint); string audience = uri.Scheme + "://" + uri.Host; Dictionary <string, string> vapidHeaders = VapidHelper.GetVapidHeaders(audience, currentVapidDetails.Subject, currentVapidDetails.PublicKey, currentVapidDetails.PrivateKey); //request.Headers.Add(@"Authorization", vapidHeaders["Authorization"]); request.Headers.Authorization = new AuthenticationHeaderValue(vapidHeaders["Authorization"].Split(" ")[0], vapidHeaders["Authorization"].Split(" ")[1]); if (String.IsNullOrEmpty(cryptoKeyHeader)) { cryptoKeyHeader = vapidHeaders["Crypto-Key"]; } else { cryptoKeyHeader += @";" + vapidHeaders["Crypto-Key"]; } } request.Headers.Add("Crypto-Key", cryptoKeyHeader); return(request); }
/// <summary> /// To get a request without sending a push notification call this method. /// This method will throw an ArgumentException if there is an issue with the input. /// </summary> /// <param name="subscription">The PushSubscription you wish to send the notification to.</param> /// <param name="payload">The payload you wish to send to the user</param> /// <param name="options"> /// Options for the GCM API key and vapid keys can be passed in if they are unique for each /// notification. /// </param> /// <returns>A HttpRequestMessage object that can be sent.</returns> public HttpRequestMessage GenerateRequestDetails( PushSubscription subscription, string payload, Dictionary <string, object> options = null, string googleFirebaseAppId = null, string googleFirebaseSenderId = null) { if (!Uri.IsWellFormedUriString(subscription.Endpoint, UriKind.Absolute)) { throw new ArgumentException(@"You must pass in a subscription with at least a valid endpoint"); } var request = new HttpRequestMessage(HttpMethod.Post, subscription.Endpoint); if (!string.IsNullOrEmpty(payload) && (string.IsNullOrEmpty(subscription.Auth) || string.IsNullOrEmpty(subscription.P256DH))) { throw new ArgumentException( @"To send a message with a payload, the subscription must have 'auth' and 'p256dh' keys."); } var currentGcmApiKey = _gcmApiKey; var currentVapidDetails = _vapidDetails; var timeToLive = DefaultTtl; var extraHeaders = new Dictionary <string, object>(); if (options != null) { var validOptionsKeys = new List <string> { "headers", "gcmAPIKey", "vapidDetails", "TTL" }; foreach (var key in options.Keys) { if (!validOptionsKeys.Contains(key)) { throw new ArgumentException(key + " is an invalid options. The valid options are" + string.Join(",", validOptionsKeys)); } } if (options.ContainsKey("headers")) { var headers = options["headers"] as Dictionary <string, object>; if (headers == null) { throw new ArgumentException("options.headers must be of type Dictionary<string,object>"); } extraHeaders = headers; } if (options.ContainsKey("gcmAPIKey")) { var gcmApiKey = options["gcmAPIKey"] as string; if (gcmApiKey == null) { throw new ArgumentException("options.gcmAPIKey must be of type string"); } currentGcmApiKey = gcmApiKey; } if (options.ContainsKey("vapidDetails")) { var vapidDetails = options["vapidDetails"] as VapidDetails; if (vapidDetails == null) { throw new ArgumentException("options.vapidDetails must be of type VapidDetails"); } currentVapidDetails = vapidDetails; } if (options.ContainsKey("TTL")) { var ttl = options["TTL"] as int?; if (ttl == null) { throw new ArgumentException("options.TTL must be of type int"); } //at this stage ttl cannot be null. timeToLive = (int)ttl; } } string cryptoKeyHeader = null; request.Headers.Add("TTL", timeToLive.ToString()); foreach (var header in extraHeaders) { request.Headers.Add(header.Key, header.Value.ToString()); } if (!string.IsNullOrEmpty(payload)) { if (string.IsNullOrEmpty(subscription.P256DH) || string.IsNullOrEmpty(subscription.Auth)) { throw new ArgumentException( @"Unable to send a message with payload to this subscription since it doesn't have the required encryption key"); } var encryptedPayload = Encryptor.Encrypt(subscription.P256DH, subscription.Auth, payload); request.Content = new ByteArrayContent(encryptedPayload.Payload); request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); request.Content.Headers.ContentLength = encryptedPayload.Payload.Length; request.Content.Headers.ContentEncoding.Add("aesgcm"); request.Headers.Add("Encryption", "salt=" + encryptedPayload.Base64EncodeSalt()); cryptoKeyHeader = @"dh=" + encryptedPayload.Base64EncodePublicKey(); } else { request.Content = new ByteArrayContent(new byte[0]); request.Content.Headers.ContentLength = 0; } var uri = new Uri(subscription.Endpoint); var audience = uri.Scheme + @"://" + uri.Host; var vapidHeaders = VapidHelper.GetVapidHeaders(audience, currentVapidDetails.Subject, currentVapidDetails.PublicKey, currentVapidDetails.PrivateKey); var serverKey = string.Format("key={0}", googleFirebaseAppId); var senderId = string.Format("id={0}", googleFirebaseSenderId); if (audience.StartsWith(@"https://updates.push.services.mozilla.com")) { request.Headers.Add(@"Authorization", vapidHeaders["Authorization"]); } else { request.Headers.TryAddWithoutValidation("Authorization", serverKey); request.Headers.TryAddWithoutValidation("Sender", senderId); } var isGcm = subscription.Endpoint.StartsWith(@"https://fcm.googleapis.com/fcm/send"); if (!isGcm && currentVapidDetails != null) { if (string.IsNullOrEmpty(cryptoKeyHeader)) { cryptoKeyHeader = vapidHeaders["Crypto-Key"]; } else { cryptoKeyHeader += @";" + vapidHeaders["Crypto-Key"]; } } request.Headers.Add("Crypto-Key", cryptoKeyHeader); return(request); }
public async Task Send() { var vapidDetails = new VapidDetails(_webPushNotificationSettings.Subject, _webPushNotificationSettings.PublicKey, _webPushNotificationSettings.PrivateKey); var webPushClient = new WebPushClient(); foreach (var pushUser in _pushUsers) { var pushSubscription = pushUser.PushSubscription; var subscription = new WebPush.PushSubscription(pushSubscription.Endpoint, pushSubscription.Keys.P256dh, pushSubscription.Keys.Auth); foreach (var roadWay in pushUser.RoadWays) { var previousRoadWay = _lastSentNotifications.FirstOrDefault(l => l.PushSubscription.Endpoint == pushUser.PushSubscription.Endpoint).RoadWays.FirstOrDefault(r => r.HmLocation == roadWay.HmLocation); if (JsonConvert.SerializeObject(roadWay) == JsonConvert.SerializeObject(previousRoadWay)) { continue; } foreach (var subscribedVms in roadWay.VariableMessageSigns) { var id = subscribedVms.Id; var liveVms = _liveData.SingleOrDefault(l => l.Id == id); var sign = liveVms != null ? liveVms.Sign : "unknown"; subscribedVms.Sign = Reformat(id, sign); } var body = string.Join(' ', roadWay.VariableMessageSigns); string image = null; var lastSign = roadWay.VariableMessageSigns.Last().Sign; var isLaneSpecific = !(lastSign.StartsWith("TP|") || lastSign.StartsWith("live/")); if (!isLaneSpecific) { if (lastSign.StartsWith("TP|")) { body = lastSign; } else if (lastSign.StartsWith("live/")) { image = string.Format("{0}/{1}", _webUrl, lastSign); } } var notification = new Notification { Title = roadWay.HmLocation, Icon = "images/xanland.png", Body = isLaneSpecific || lastSign.StartsWith("TP|") ? body : "Klik op de afbeelding voor de volledige beeldstand.", Image = isLaneSpecific ? null : image, Tag = isLaneSpecific ? string.Format("{0}={1}@{2}", roadWay.HmLocation, DateTime.UtcNow.Hour, DateTime.UtcNow.Day) : roadWay.HmLocation, Data = new NotificationData { CoordinatesUrl = roadWay.Coordinates != null?string.Format("{0}/?lat={1}&lon={2}&zoom=17", _webUrl, roadWay.Coordinates.X.ToString().Replace(',', '.'), roadWay.Coordinates.Y.ToString().Replace(',', '.')) : null, }, Actions = new List <NotificationAction> { new NotificationAction { Action = "go-to-location", Icon = "images/xanland.png", Title = "Open locatie" } }, HmLocation = roadWay.HmLocation, LanesShownSign = string.Join(' ', roadWay.VariableMessageSigns), DripShownSign = false }; try { await webPushClient.SendNotificationAsync(subscription, JsonConvert.SerializeObject(notification, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }), vapidDetails); } catch (Exception) { } } } PostProcess(); }
/// <summary> /// To send a push notification call this method with a subscription, optional payload and any options /// Will exception is unsuccessful /// </summary> /// <param name="subscription">The PushSubscription you wish to send the notification to.</param> /// <param name="payload">The payload you wish to send to the user</param> /// <param name="gcmAPIKey">The GCM API key</param> public void SendNotification(PushSubscription subscription, string payload, string gcmAPIKey) { SendNotificationAsync(subscription, payload, gcmAPIKey).Wait(); }
/// <summary> /// To send a push notification call this method with a subscription, optional payload and any options /// Will exception is unsuccessful /// </summary> /// <param name="subscription">The PushSubscription you wish to send the notification to.</param> /// <param name="payload">The payload you wish to send to the user</param> /// <param name="options">Options for the GCM API key and vapid keys can be passed in if they are unique for each notification.</param> public void SendNotification(PushSubscription subscription, string payload = null, Dictionary <string, object> options = null) { SendNotificationAsync(subscription, payload, options).Wait(); }
/// <summary> /// To send a push notification call this method with a subscription, optional payload and any options /// Will exception is unsuccessful /// </summary> /// <param name="subscription">The PushSubscription you wish to send the notification to.</param> /// <param name="payload">The payload you wish to send to the user</param> /// <param name="vapidDetails">The vapid details for the notification.</param> public void SendNotification(PushSubscription subscription, string payload, VapidDetails vapidDetails) { SendNotificationAsync(subscription, payload, vapidDetails).Wait(); }
public WebPushException(string message, PushSubscription pushSubscription, HttpResponseMessage responseMessage) : base(message) { PushSubscription = pushSubscription; HttpResponseMessage = responseMessage; }
public WebPushException(string message, HttpStatusCode statusCode, HttpResponseHeaders headers, PushSubscription pushSubscription) : base(message) { StatusCode = statusCode; Headers = headers; PushSubscription = pushSubscription; }
/// <summary> /// To send a push notification call this method with a subscription, optional payload and any options /// Will exception if unsuccessful /// </summary> /// <param name="subscription">The PushSubscription you wish to send the notification to.</param> /// <param name="payload">The payload you wish to send to the user</param> /// <param name="options"> /// Options for the GCM API key and vapid keys can be passed in if they are unique for each /// notification. /// </param> public void SendNotification(PushSubscription subscription, string payload = null, Dictionary <string, object> options = null) { SendNotificationAsync(subscription, payload, options).ConfigureAwait(false).GetAwaiter().GetResult(); }