Skip to content

not working in Chrome #33

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
xbranko opened this issue Jun 20, 2017 · 15 comments
Closed

not working in Chrome #33

xbranko opened this issue Jun 20, 2017 · 15 comments

Comments

@xbranko
Copy link

xbranko commented Jun 20, 2017

Following the article about sending web push notifications I managed to make it work in both Chrome and Firefox browsers. Using the webpush-java I managed to make it send notifications to Firefox, but not to Chrome. When sending to Chrome, I am getting return code 400 UnauthorizedRegistration.

The web server I use is web server for Chrome, as suggested in the article. I use the corresponding applicationServerPublicKey, as generated in the companion site, and use corresponding public/private keys (different for Firefox and Chrome).

Searching the internet didn't yield any actionable steps. Any suggestions as to what to try/how to get to the bottom of this? Please let me know if any more/additional information is needed.

Thanks!

@martijndwars
Copy link
Member

Can you share the Java code that you use to send the notification?

@xbranko
Copy link
Author

xbranko commented Jun 20, 2017

Here is the simplified version of the class (that is part of a much bigger system) used for sending browser push notification.

import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;

import javax.annotation.PostConstruct;

import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpResponse;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import org.springframework.beans.factory.annotation.Autowired;

import nl.martijndwars.webpush.Notification;
import nl.martijndwars.webpush.PushService;
import nl.martijndwars.webpush.Subscription;
import nl.martijndwars.webpush.Utils;

public class BrowserClient
{
  public void send(final String browserEndPoint, final String browserPublicKey, final String browserPrivateKey, final String browserPayload)
  {
    final Subscription subscription = new Subscription();
    subscription.keys = subscription.new Keys();

    subscription.endpoint    = browserEndPoint;
    subscription.keys.p256dh = browserPublicKey;
    subscription.keys.auth   = browserPrivateKey;
    final String payload     = browserPayload;

    sendPushMessage(subscription, payload);
  }

  private void sendPushMessage(final Subscription subscription, final String payload)
  {
    try
    {
      final Notification notification = new Notification(subscription.endpoint,
                                                         getUserPublicKey(Base64.decodeBase64(subscription.keys.p256dh)),
                                                         Base64.decodeBase64(subscription.keys.auth),
                                                         payload.getBytes());
      final PushService pushService = new PushService();
      pushService.setPublicKey(publicKey);
      pushService.setPrivateKey(privateKey);
      final HttpResponse response = pushService.send(notification);
      if(response.getStatusLine().getStatusCode() != 201)
      {
        throw new RuntimeException(response.toString());
      }
    }
    catch(final RuntimeException e)
    {
      throw e;
    }
    catch(final Exception e)
    {
      throw new RuntimeException(payload, e);
    }
  }

  private PublicKey getUserPublicKey(final byte[] key) throws InvalidKeySpecException
  {
    final ECPoint point           = ecSpec.getCurve().decodePoint(key);
    final ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, ecSpec);

    return keyFactory.generatePublic(pubSpec);
  }

  @PostConstruct
  private void init()
  {
    try
    {
      if(Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null)
      {
        Security.addProvider(new BouncyCastleProvider());
      }
      keyFactory = KeyFactory.getInstance("ECDH", BouncyCastleProvider.PROVIDER_NAME);
      ecSpec     = ECNamedCurveTable.getParameterSpec("secp256r1");
      if(ecSpec == null)
      {
        throw new RuntimeException("ecSpec == null");
      }
      publicKey = Utils.loadPublicKey(browserPublicKey);
      privateKey = Utils.loadPrivateKey(browserPrivateKey);
    }
    catch(final Exception e)
    {
      throw new RuntimeException("init", e);
    }
  }

  @Autowired
  private String browserPublicKey;

  @Autowired
  private String browserPrivateKey;

  private PublicKey publicKey;
  private PrivateKey privateKey;
  private ECParameterSpec ecSpec;
  private KeyFactory keyFactory;
}

@martijndwars
Copy link
Member

martijndwars commented Jun 22, 2017

I tried to reproduce your problem with the latest version of this library, but was unable to do so. I took the following steps:

  1. I used the push companion website to generate a public and private key (BLzPK96e2_tX5pE9HA9D6j_H1fkZi3yEgpG1HGifioFtM1wWSoJBcV7vWAsXzIVngaVAm5lmnD2TwvF46ouYx0M and GtGG-dCPNMi5nTw2jwsvHnTIGIeQudJW26uIoYgglDs).
  2. I configured my website to use the public key.
  3. I cleared all existing service workers at chrome://serviceworker-internals/ so I'm sure Chrome will give me a new subscription.
  4. I opened http://127.0.0.1:8080/ and took note of the subscription:
{"endpoint":"https://fcm.googleapis.com/fcm/send/epl45v0X9BE:APA91bG8kPTgi8ZP67QlXBU8Qwfp_ONhM1xDBs9quNcb-lZYF5OSsdDYSvS6qHXaEjag9uhi_np7KIbOZ-vheIOKunPHl3huEeb8Emc_gcmN5RBYiSfVx3Bgv6DRNZHnLqqwL-wdfR8H","keys":{"p256dh":"BP8-PfGgU6Vt3ZcSZEaY_PLUTWMOc4JLtpcwDdX1qEY-AVk4g4qg8Mk2a_69a1-1Dz87gVAmmOCOjG-2BCZotfQ=","auth":"OoWUPKs8kQK7sE69l-po6A=="}}
  1. I created the following code:
Security.addProvider(new BouncyCastleProvider());

String publicKey = "BLzPK96e2_tX5pE9HA9D6j_H1fkZi3yEgpG1HGifioFtM1wWSoJBcV7vWAsXzIVngaVAm5lmnD2TwvF46ouYx0M";
String privateKey = "GtGG-dCPNMi5nTw2jwsvHnTIGIeQudJW26uIoYgglDs";

Subscription subscription = new Gson().fromJson("{\"endpoint\":\"https://fcm.googleapis.com/fcm/send/dCClbSTzmVs:APA91bHKRjr11h7NzhpmwkVLHoa2EX6a30CrbRGS5yGeE_-uRCJA7FWwn52gnzbDB9gnI_MeBAJcLQ0Ad_T-Z0JK2Tbno3b28BU8F3nQQnefzB2lrBt21Yn6irUCyQw7p6hzq9MVOiTB\",\"keys\":{\"p256dh\":\"BEyx_O7C0K8tXrn2z6XWWPvxjLK_D-usNDSzTwlDe3XMa5oPTTSNxnzFTLsulM5orOZigO6oZxCtib8gCPTfVv8=\",\"auth\":\"MUPHKjaPCkVgtbBV8Ywb7g==\"}}", Subscription.class);
Notification notification = new Notification(subscription, "Hello, world");

PushService pushService = new PushService();
pushService.setPublicKey(publicKey);
pushService.setPrivateKey(privateKey);
pushService.setSubject("mailto:[email protected]");

HttpResponse httpResponse = pushService.send(notification);

System.out.println(httpResponse.getStatusLine().getStatusCode());
System.out.println(IOUtils.toString(httpResponse.getEntity().getContent(), StandardCharsets.UTF_8));

When running this code, the message arrives in Chrome 59.0.

Let me know if this is working for you.

@askondro
Copy link

I did the exact procedure you described here but I'm getting:
"key spec not recognised"

Mind you, in v3.0.0 PushService.setPrivateKey and setPublicKey only accept PrivateKey and PublicKey objects so I had to pass them in the PushService contructor. Everything in the code is the same as in yours.

The exception occurs while executing:

PushService service = new PushService(publicKey, privateKey, "IPT");

        final static String publicKey = "BBDB9Cvfm9pA1RK6Tuv_p_xjHqPjpAgGTpnBjQMGxWRiTmz7bfjagEMzVsME5kT1kxTd_Y7dC0DgSjwhSv7iss0";
 	final static String privateKey = "pjMhI8OEJOvjBK5ODX3R4-XexW8XIzhztWtMx2tN1go";

	public void sendNotification(Member member) {
		if (member.getSubscription() == null)
			return;
		
		Security.addProvider(new BouncyCastleProvider());
		
		NotificationSubscription notificationSubscription = member.getSubscription();

		Subscription subscription = new Subscription();
		subscription.endpoint = notificationSubscription.getEndpoint();
		subscription.keys = subscription.new Keys();
		subscription.keys.auth = notificationSubscription.getKeys().getAuth();
		subscription.keys.p256dh = notificationSubscription.getKeys().getP256dh();
		
		try {
			PushService service = new PushService(publicKey, privateKey, "IPT");
			Notification notification = new Notification(subscription, "Task updated");
			service.send(notification);
		} catch (GeneralSecurityException | IOException | JoseException | ExecutionException | InterruptedException e) {
			//e.printStackTrace();
			System.out.println("Exception PushService: " + e.getMessage());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

@patrickathompson
Copy link

patrickathompson commented Oct 18, 2017

I am also having the same issue. I have tested using the exact same encryption keys and subscription objects against the nodejs module vs this java library. I get the 400s in Chrome when using the java library.

NodeJS w/ GC Subscription:

$ web-push send-notification --endpoint="https://fcm.googleapis.com/fcm/send/dV7wNveT2wA:APA91bGsHJuVbE6jDu7kvGv80FgR2fMzRH5ZQ-uZgrKSX_79xkqv-nyro3QLXe-VRd9pdv_XAzVjVqUbOkUoulXIazSuNtYzmZhntZlLSvKqHBqB-3pNYT-G_Ec4CKeUmnOATFqXPEDF" --auth="8fI1K6u0wTeH9kjiozST7g==" --key="BHT4acE7T7c_eg00971qTB-8TzvK-hP-rFZREluX3pbqDfZlLnVgnQ9v69sTS2V4YwbYzxO_p88n1pNizt6mhFQ=" --payload='{"title": "Welcome to Newsletter!", "body": "We are excited that you are a subscriber of our newsletter. Keep an eye out for more updates to come. Enjoy!","url": "https://www.mydomain.com","badge": "https://browserpush-app.mydomain.com/sw-library/images/logo.png","icon": "https://browserpush-app.mydomain.com/sw-library/images/logo.png"}' --vapid-subject=http://www.postup.com --vapid-pvtkey='yeRaKKVytk3BCNBXr-cyYtrcmiL9X6xYgE84DLF6RU0' --vapid-pubkey="BLH4W0FKiQj1LWbjz50A-koqoxCfMEawOtMThC9d5cgG5Lplc70v13_fa42dxSmvsvRxjmQ2B-d3QRQJ9NAyrPc"

Push message sent.

Java w/ GC Subscription:

$ java -jar ./web-push-3.0.1-all.jar send-notification --subscription '{"endpoint":"https://fcm.googleapis.com/fcm/send/dV7wNveT2wA:APA91bGsHJuVbE6jDu7kvGv80FgR2fMzRH5ZQ-uZgrKSX_79xkqv-nyro3QLXe-VRd9pdv_XAzVjVqUbOkUoulXIazSuNtYzmZhntZlLSvKqHBqB-3pNYT-G_Ec4CKeUmnOATFqXPEDF","expirationTime":null,"keys":{"p256dh":"BHT4acE7T7c_eg00971qTB-8TzvK-hP-rFZREluX3pbqDfZlLnVgnQ9v69sTS2V4YwbYzxO_p88n1pNizt6mhFQ=","auth":"8fI1K6u0wTeH9kjiozST7g=="}}' --payload '{"title": "Welcome to Newsletter!", "body": "We are excited that you are a subscriber of our newsletter. Keep an eye out for more updates to come. Enjoy!","url": "https://www.mydomain.com","badge": "https://browserpush-app.mydomain.com/sw-library/images/logo.png","icon": "https://browserpush-app.mydomain.com/sw-library/images/logo.png"}' --privateKey yeRaKKVytk3BCNBXr-cyYtrcmiL9X6xYgE84DLF6RU0 --publicKey BLH4W0FKiQj1LWbjz50A-koqoxCfMEawOtMThC9d5cgG5Lplc70v13_fa42dxSmvsvRxjmQ2B-d3QRQJ9NAyrPc

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

HTTP/1.1 400 UnauthorizedRegistration [Content-Type: text/html; charset=UTF-8, Date: Fri, 06 Oct 2017 21:29:20 GMT, Expires: Fri, 06 Oct 2017 21:29:20 GMT, Cache-Control: private, max-age=0, X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN, X-XSS-Protection: 1; mode=block, Server: GSE, Alt-Svc: quic=":443"; ma=2592000; v="39,38,37,35", Accept-Ranges: none, Vary: Accept-Encoding, Transfer-Encoding: chunked] [Chunked: false]

@martijndwars
Copy link
Member

Thanks for the report @patrickathompson. I did not find the time to look into this yet, but I will do so as soon as possible.

@agustinbreit
Copy link

Same for me, I can't meka it work yet, I'm getting HTTP/1.1 400 UnauthorizedRegistration on each request

@agustinbreit
Copy link

more information by testing chrome:
{"code": 401, "errno": 109, "error": "Unauthorized", "more_info": "http://autopush.readthedocs.io/en/latest/http.html#error-codes", "message": "Request did not validate Invalid Authorization Header"}

@patrickathompson
Copy link

To be fair, it has worked for me in the past. At first, I thought it was something I was doing wrong, or the code my application was using. After following the steps @martijndwars mentions in his Jun 22nd post several times, I am getting intermittent results. Sometimes it works and sometimes it doesn't. It's also worth noting, that when it does work, it will always work, and when it doesn't work, it will never work. I have not been able to identify any patterns other than that. I have provided an example scenario where the 400 issue occurs that does not use any of my own code. Hopefully that should be enough evidence to investigate the issue. I have also done some code analysis and compared the java source against the nodejs source, and I am noticing some deviations in the algorithms used. I don't quite understand everything about how the authentication works, or I would have attempted a patch, but I can clearly see the java library is authenticating in a different manner than the nodejs module.

@martijndwars
Copy link
Member

Sometimes it works and sometimes it doesn't. It's also worth noting, that when it does work, it will always work, and when it doesn't work, it will never work.

This could be a problem with the keys (or the way we encode/decode them in Java code). I remember the NodeJS version had a problem when the base64 encoding needed padding. Thanks for the extra info, I'll look into this.

@thesawdoctor
Copy link

thesawdoctor commented Jan 29, 2018

We have experienced almost the exact same situation as @patrickathompson. Three sets of keys, all work with the NodeJS lib, one set works consistently with the Java lib, the other two fail consistently with the Java lib.

I believe that the problem with our keys is arising from the use of ECDH as the algorithm to load the VAPID public key and private key. We have tested switching this to EC and found that all three sets of keys were able to send web pushes successfully.

@martijndwars
Copy link
Member

Three sets of keys, all work with the NodeJS lib, one set works consistently with the Java lib, the other two fail consistently with the Java lib.

Interesting. How did you generate these three keys?

@thesawdoctor
Copy link

Two sets (one working, one not) were generated using the NodeJS web push library:
https://github.com/web-push-libs/web-push
The other belongs to a third party, I don't have information on how that set was generated. That set was not working.

@desmethans
Copy link

When i make the VAPID keys with the node library, the c# library or on https://web-push-codelab.glitch.me/ my application works with he node library, the c# library but not with the java library (i get the problems mentioned above)

When i make the VAPID keys with openssl my application works with he node library, the c# library and also with the java library

@martijndwars
Copy link
Member

martijndwars commented Feb 1, 2018

Ah, I figured out what is going wrong. What happens is that (1) the base64-encoded private key is converted to an array of bytes and (2) this array of bytes is converted to a BigInteger. However, during (2) the array of bytes is assumed to represent two's complement binary, but it actually represents unsigned binary.

I've released version 3.0.2 that fixes this issue. You can now use the keys from web-push-codelab.glitch.me without any problems. I'm closing the issue, but feel free to leave a comment if there's still a problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants