Giter VIP home page Giter VIP logo

webpush's Introduction

WebPush

Lightweight Kotlin library for sending web push notifications with zero external dependencies.

This library by default uses blocking HTTP client provided in the JDK, but you can also use it only to build your requests and combine it with any HTTP library that suits your needs.

Installation

Stable releases are available in Maven Central repository.

Latest Maven Central version badge

For Gradle, add the following to your gradle.build.kts file:

dependencies {
    implementation("com.interaso:webpush:1.2.0")
}

For Maven, add the following to the dependencies block of your pom.xml file:

<dependency>
    <groupId>com.interaso</groupId>
    <artifactId>webpush</artifactId>
    <version>1.2.0</version>
</dependency>

Usage

Sending notifications

The process starts with initializing the WebPushService with a subject (URL or mailto: prefixed e-mail address) and a set of VAPID keys, which are covered later in this document.

val pushService = WebPushService(
    subject = "https://example.com", // or "mailto:[email protected]"
    vapidKeys = VapidKeys.generate()
)

Once the service is set up, you're ready to send a push notification.

val subscriptionState = pushService.send(
    payload = "Example Notification",
    endpoint = subscription.endpoint, // "https://fcm.googleapis.com/fcm/send/...",
    p256dh = subscription.keys.p256dh, // "BPzdj8OB06SepRit5FpHUsaEPfs...",
    auth = subscription.keys.auth, // "hv2EhUZIbsWt8CJ...",
)

Available arguments

  • endpoint - The URL endpoint that identifies the push service subscription.
  • p256dh - The P256DH key for authentication with the push service provider.
  • auth - The authentication secret for the push service provider.
  • payload - The message payload to be sent in the push notification.
  • ttl - The time-to-live value for the push notification (optional).
  • topic - The topic of the push notification (optional).
  • urgency - The urgency level of the push notification (optional).

VAPID keys

VAPID (Voluntary Application Server Identification) keys provides a method for application servers to identify themselves to push services. This section covers the handling of these keys with VapidKeys class.

// Generate new keys
val vapidKeys = VapidKeys.generate()

// Create from Base64 encoded strings in X509 and PKCS8 formats
val vapidKeys = VapidKeys.create(
    x509PublicKey = "MIIBIjANBgkqhkiG9w0BAQEF...",
    pkcs8PrivateKey = "MIIEvQIBADANBgkqhkiG..."
)

// Create from Base64 encoded strings of uncompressed bytes
val vapidKeys = VapidKeys.fromUncompressedBytes(
    publicKey = "BJwwFRoDoOx2vQPfvbeo-m1f...",
    privateKey = "P5GjTLppISlmUyNiZqZi..."
)

// Load from file (line separated X509 and PKCS8 keys in Base64)
val vapidKeys = VapidKeys.load(
    path = Path("path/to/vapid.keys"),
    generateMissing = true, // If file not found, generate one and save it
)

// Get application server key for JavaScript
val applicationServerKey = vapidKeys.applicationServerKey

// Serialize to Base64 encoded strings in X509 and PKCS8 formats
val publicKey = vapidKeys.x509PublicKey
val privateKey = vapidKeys.pkcs8PrivateKey

Tip

For using same VAPID keys format when migrating from webpush-java library, use VapidKeys.fromUncompressedBytes() factory function.

Using custom HTTP client

You may prefer to use a different HTTP client for reasons of performance, suspendability, or familiarity. The example demonstrates how to use WebPush class to generate request headers and the encrypted body and how to process the response.

// Setup request builder with subject and VAPID keys
val webPush = WebPush(subject, vapidKeys)

// Generate request headers and encrypted body
val headers = webPush.getHeaders(endpoint, ttl, topic, urgency)
val body = webPush.getBody(payload, p256dh, auth)

// Use custom HTTP client to process request
val response = customHttpClient.post(endpoint, headers, body)

// Map status code to subscription state
val subscriptionState = webPush.getSubscriptionState(response.status)

Testing

In order to ensure the functionality and reliability, this library is automatically tested using local server and real Chromium browser. For more details see BrowserTest class.

Notification

Snapshots

Development snapshots are available in the Sonatype snapshots repository. Make sure to include it in your repositories.

Latest snapshot version badge

For Gradle, add the following to your gradle.build.kts file:

repositories {
    maven("https://s01.oss.sonatype.org/content/repositories/snapshots")
}

For Maven, add the following to the repositories block of your pom.xml file:

<repository>
    <id>sonatype-snapshots</id>
    <url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
    <releases>
        <enabled>false</enabled>
    </releases>
    <snapshots>
        <enabled>true</enabled>
    </snapshots>
</repository>

Resources

Specifications

Credit

This project is essentially a Kotlin adaptation of the webpush-java library. We extend our sincere gratitude to the original author for providing the foundation upon which this library is built.

Illustration used in this README is by vectorjuice on Freepik.

License

This project is licensed under the terms of the MIT license. See the LICENSE file for more details.

webpush's People

Contributors

andreblanke avatar dependabot[bot] avatar morki avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

webpush's Issues

Caused by: java.security.InvalidKeyException: Point is not on curve

hi, I'm currently trying to use your webpush library in my java springboot project
and having hard time figuring out what's the problem.
Try to follow the Readme.md
my part of the code is down below.
using node server to get the subscription info with same vapid keys and notificationDto has those info.
And got those vapid keys from "web-push generate-vapid-keys --json"
what could be the problem?
I guess the problem is the vapid keys but not sure.

image

Caused by: java.security.InvalidKeyException: Point is not on curve
	at jdk.crypto.ec/sun.security.ec.ECDHKeyAgreement.validate(ECDHKeyAgreement.java:200) ~[jdk.crypto.ec:na]
	at jdk.crypto.ec/sun.security.ec.ECDHKeyAgreement.engineDoPhase(ECDHKeyAgreement.java:147) ~[jdk.crypto.ec:na]
	at java.base/javax.crypto.KeyAgreement.doPhase(KeyAgreement.java:581) ~[na:na]
	at com.interaso.webpush.CryptoKt.generateEcdhSharedSecret(Crypto.kt:109) ~[webpush-1.1.0.jar:na]
	at com.interaso.webpush.WebPush.getBody(WebPush.kt:42) ~[webpush-1.1.0.jar:na]
	at com.interaso.webpush.WebPushService.send(WebPushService.kt:69) ~[webpush-1.1.0.jar:na]
	at com.drrr.infra.notifications.kafka.webpush.WebPushConsumer.consume(WebPushConsumer.java:37) ~[classes/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
	at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:169) ~[spring-messaging-6.1.0-M4.jar:6.1.0-M4]
	at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:119) ~[spring-messaging-6.1.0-M4.jar:6.1.0-M4]
	at org.springframework.kafka.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:56) ~[spring-kafka-3.0.10.jar:3.0.10]
	at org.springframework.kafka.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:375) ~[spring-kafka-3.0.10.jar:3.0.10]
	at org.springframework.kafka.listener.adapter.RecordMessagingMessageListenerAdapter.onMessage(RecordMessagingMessageListenerAdapter.java:92) ~[spring-kafka-3.0.10.jar:3.0.10]
	at org.springframework.kafka.listener.adapter.RecordMessagingMessageListenerAdapter.onMessage(RecordMessagingMessageListenerAdapter.java:53) ~[spring-kafka-3.0.10.jar:3.0.10]
	at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.doInvokeOnMessage(KafkaMessageListenerContainer.java:2873) ~[spring-kafka-3.0.10.jar:3.0.10]
	... 12 common frames omitted

I tried getting my vapid keys from, https://vapidkeys.com/
and still doesnt work
image

Keys from webpush-java not compatible

The keys that I used with the https://github.com/web-push-libs/webpush-java library cannot be used to create a VapidKey object.

Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: DerInputStream.getLength(): lengthTag=28, too big.
	at jdk.crypto.ec/sun.security.ec.ECKeyFactory.engineGeneratePublic(ECKeyFactory.java:158)
	at java.base/java.security.KeyFactory.generatePublic(KeyFactory.java:351)
	at com.interaso.webpush.CryptoKt.generatePublicKeyFromX509(Crypto.kt:81)
	at com.interaso.webpush.VapidKeys$Factory.create(VapidKeys.kt:58)
	at com.example.MainClazz.main(MainClazz.kt:47)
	at com.example.MainClazz.main(MainClazz.kt)
Caused by: java.security.InvalidKeyException: IOException: DerInputStream.getLength(): lengthTag=28, too big.
	at java.base/sun.security.x509.X509Key.decode(X509Key.java:397)
	at java.base/sun.security.x509.X509Key.decode(X509Key.java:402)
	at jdk.crypto.ec/sun.security.ec.ECPublicKeyImpl.<init>(ECPublicKeyImpl.java:74)
	at jdk.crypto.ec/sun.security.ec.ECKeyFactory.implGeneratePublic(ECKeyFactory.java:225)
	at jdk.crypto.ec/sun.security.ec.ECKeyFactory.engineGeneratePublic(ECKeyFactory.java:154)
	... 5 more

For reference I created a new key pair using the following page: https://www.attheminute.com/vapid-key-generator
I am not sure if my keys were originally created with such a page but the format and length of the keys looks similar to mine.

These are the example keys I tried:

val public = "BJwwFRoDoOx2vQPfvbeo-m1fZZHo6lIjtyTlWHjLNSCtHuWdGryZD5xt0LeawVQq7G60ioID1sC33fEoQT8jCzg"
val private = "P5GjTLppISlmUyNiZqZi0HNq7GXFniAdcBECNsKBxfI"

It would be great if there was a way to migrate to this library and keep using my keys like maybe converting them into a format that this library can use. Otherwise all users would have to re-enable the webpush subscription.

Make this library multiplatform

To make this library multiplatform, we need to replace this parts with multiplatform implementation:

Blockers in https://github.com/whyoleg/cryptography-kotlin:

  • support for ECDH (key agreement)
  • support for HKDF (key derivation)

There will be some breaking changes, so it will be version 2.0 or another library (webpush-multiplatform).

Expose HTTP status code when throwing WebPushException

It might be useful to expose the HTTP status code which caused a WebPushException to be thrown as a member of the class, e.g. by introducing a statusCode field:

public class WebPushException(val statusCode: Int, message: String, cause: Throwable? = null)
    : Exception(message, cause)

This would allow further handling of the exception without having to resort to parsing the exception message to extract the status code. Since this would strongly associate the WebPushException with an unsuccessful HTTP status code, it might be better to introduce a a new WebPushException subclass for this to keep the WebPushException class more generic.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.