webpush 0.2.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7b8ab0b45116811a01dc8d7cd726639b4b4f5781
4
- data.tar.gz: f684010fd4cd1f75d91affaa73728ba9a45b705e
3
+ metadata.gz: ef85cc1d7d9a39a57426c5d1d20b89c0c5069407
4
+ data.tar.gz: 5b964e6fb1bec69c5bb3f331d139ab4b848d063e
5
5
  SHA512:
6
- metadata.gz: d4c143e92b6b95a6030cca488c2b270cd348a12332b22eaacb987346b9cffc373523b860a434ea9756519df68576a42521ab37663d8084a62ab22c38406f743a
7
- data.tar.gz: 48baaa4360ce374f4f2f171bae6235d76e978e1e2db6ca622893058d01e7a7cf4513cf856d19c91dbf8f66638874d3a25ff0a61a435204dac86406f780280471
6
+ metadata.gz: ca808369991e12634181e0a748ea1c3f8a918ec370f14f7709820a9004305c041dab21b571d610d3db71fdd4ac382f2166641f1375ca345b267db134fbb351be
7
+ data.tar.gz: 0e95d566e542ce200ee670c12a932d8de8b76f69df13115785fc59d10a8b897df9734b48382656ac97263cbbea23003e92bb4df00fe55e2f214af88964743ae1
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2016 Hiroyuki Sakuraba
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![Build Status](https://travis-ci.org/zaru/webpush.svg?branch=master)](https://travis-ci.org/zaru/webpush)
5
5
  [![Gem Version](https://badge.fury.io/rb/webpush.svg)](https://badge.fury.io/rb/webpush)
6
6
 
7
- This Gem will send the Web Push API. It supports the encryption necessary to payload.
7
+ This gem makes it possible to send push messages to web browsers from Ruby backends using the [Web Push Protocol](https://tools.ietf.org/html/draft-ietf-webpush-protocol-10). It supports [Message Encryption for Web Push](https://tools.ietf.org/html/draft-ietf-webpush-encryption) to send messages securely from server to user agent.
8
8
 
9
9
  Payload is supported by Chrome50+, Firefox48+.
10
10
 
@@ -26,7 +26,188 @@ Or install it yourself as:
26
26
 
27
27
  ## Usage
28
28
 
29
- ### using the payload
29
+ Sending a web push message to a visitor of your website requires a number of steps:
30
+
31
+ 1. Your server has (optionally) generated (one-time) a set of [Voluntary Application server Identification (VAPID)](https://tools.ietf.org/html/draft-ietf-webpush-vapid-01) keys.
32
+ 2. To send messages through Chrome, you have registered your site through the [Google Developer Console](https://console.developers.google.com/) and have obtained a GCM sender id. For using Google's deprecated GCM protocol instead of VAPID, a separate GCM API key from your app settings would also be necessary.
33
+ 3. A 'manifest.json' file, linked from your user's page, identifies your app settings, including the GCM sender ID.
34
+ 5. Also in the user's web browser, a `serviceWorker` is installed and activated and its `pushManager` property is subscribed to push events with your VAPID public key, with creates a `subscription` JSON object on the client side.
35
+ 6. Your server uses the `webpush` gem to send a notification with the `subscription` obtained from the client and an optional payload (the message).
36
+ 7. Your service worker is set up to receive `'push'` events. To trigger a desktop notification, the user has accepted the prompt to receive notifications from your site.
37
+
38
+ ### Generating VAPID keys
39
+
40
+ Use `webpush` to generate a VAPID key that has both a `public_key` and `private_key` attribute to be saved on the server side.
41
+
42
+ ```ruby
43
+ # One-time, on the server
44
+ vapid_key = Webpush.generate_key
45
+
46
+ # Save these in your application server settings
47
+ vapid_key.public_key
48
+ vapid_key.private_key
49
+ ```
50
+
51
+ ### Declaring manifest.json
52
+
53
+ For Chrome web push, add the GCM sender id to a `manifest.json` file served at the scope of your app (or above), like at the root.
54
+
55
+ ```javascript
56
+ {
57
+ "name": "My Website",
58
+ "gcm_sender_id": "1006629465533"
59
+ }
60
+ ```
61
+
62
+ And link to it somewhere in the `<head>` tag:
63
+
64
+ ```html
65
+ <!-- index.html -->
66
+ <link rel="manifest" href="/manifest.json" />
67
+ ```
68
+
69
+ ### Installing a service worker
70
+
71
+ Your application javascript must register a service worker script at an appropriate scope (we're sticking with the root).
72
+
73
+ ```javascript
74
+ // application.js
75
+ // Register the serviceWorker script at /serviceworker.js from your server if supported
76
+ if (navigator.serviceWorker) {
77
+ navigator.serviceWorker.register('/serviceworker.js')
78
+ .then(function(reg) {
79
+ console.log('Service worker change, registered the service worker');
80
+ });
81
+ }
82
+ // Otherwise, no push notifications :(
83
+ else {
84
+ console.error('Service worker is not supported in this browser');
85
+ }
86
+ ```
87
+
88
+ ### Subscribing to push notifications
89
+
90
+ The VAPID public key you generated earlier is made available to the client as a `UInt8Array`. To do this, one way would be to expose the urlsafe-decoded bytes from Ruby to JavaScript when rendering the HTML template. (Global variables used here for simplicity).
91
+
92
+ ```javascript
93
+ window.vapidPublicKey = new Uint8Array(<%= Base64.urlsafe_decode64(ENV['VAPID_PUBLIC_KEY']).bytes %>);
94
+ ```
95
+
96
+ Your application javascript would then use the `navigator.serviceWorker.pushManager` to subscribe to push notifications, passing the VAPID public key to the subscription settings.
97
+
98
+ ```javascript
99
+ // application.js
100
+ // When serviceWorker is supported, installed, and activated,
101
+ // subscribe the pushManager property with the vapidPublicKey
102
+ navigator.serviceWorker.ready.then((serviceWorkerRegistration) => {
103
+ serviceWorkerRegistration.pushManager
104
+ .subscribe({
105
+ userVisibleOnly: true,
106
+ applicationServerKey: window.vapidPublicKey
107
+ });
108
+ });
109
+ ```
110
+
111
+ Note: If you will not be sending VAPID details, then there is no need generate VAPID
112
+ keys, and the `applicationServerKey` parameter may be omitted from the
113
+ `pushManager.subscribe` call.
114
+
115
+ ### Triggering a web push notification
116
+
117
+ Hook into an client-side or backend event in your app to deliver a push message. The server must be made aware of the `subscription`. In the example below, we send the JSON generated subscription object to our backend at the "/push" endpoint with a message.
118
+
119
+ ```javascript
120
+ // application.js
121
+ // Send the subscription and message from the client for the backend
122
+ // to set up a push notification
123
+ $(".webpush-button").on("click", (e) => {
124
+ navigator.serviceWorker.ready
125
+ .then((serviceWorkerRegistration) => {
126
+ serviceWorkerRegistration.pushManager.getSubscription()
127
+ .then((subscription) => {
128
+ $.post("/push", { subscription: subscription.toJSON(), message: "You clicked a button!" });
129
+ });
130
+ });
131
+ ```
132
+
133
+ Imagine a Ruby app endpoint that responds to the request by triggering notification through the `webpush` gem.
134
+
135
+ ```ruby
136
+ # app.rb
137
+ # Use the webpush gem API to deliver a push notiifcation merging
138
+ # the message, subscription values, and vapid options
139
+ post "/push" do
140
+ Webpush.payload_send(
141
+ message: params[:message]
142
+ endpoint: params[:subscription][:endpoint],
143
+ p256dh: params[:subscription][:keys][:p256dh],
144
+ auth: params[:subscription][:keys][:p256dh],
145
+ vapid: {
146
+ subject: "mailto:sender@example.com",
147
+ public_key: ENV['VAPID_PUBLIC_KEY'],
148
+ private_key: ENV['VAPID_PRIVATE_KEY']
149
+ }
150
+ )
151
+ end
152
+ ```
153
+
154
+ Note: the VAPID options should be omitted if the client-side subscription was
155
+ generated without the `applicationServerKey` parameter described earlier.
156
+
157
+ ### Receiving the push event
158
+
159
+ Your `/serviceworker.js` script can respond to `'push'` events. One action it can take is to trigger desktop notifications by calling `showNotification` on the `registration` property.
160
+
161
+ ```javascript
162
+ // serviceworker.js
163
+ // The serviceworker context can respond to 'push' events and trigger
164
+ // notifications on the registration property
165
+ self.addEventListener("push", (event) => {
166
+ let title = (event.data && event.data.text()) || "Yay a message";
167
+ let body = "We have received a push message";
168
+ let tag = "push-simple-demo-notification-tag";
169
+ let icon = '/assets/my-logo-120x120.png';
170
+
171
+ event.waitUntil(
172
+ self.registration.showNotification(title, { body, icon, tag })
173
+ )
174
+ });
175
+ ```
176
+
177
+ Before the notifications can be displayed, the user must grant permission for [notifications](https://developer.mozilla.org/en-US/docs/Web/API/notification) in a browser prompt, using something like the example below.
178
+
179
+ ```javascript
180
+ // application.js
181
+
182
+ // Let's check if the browser supports notifications
183
+ if (!("Notification" in window)) {
184
+ console.error("This browser does not support desktop notification");
185
+ }
186
+
187
+ // Let's check whether notification permissions have already been granted
188
+ else if (Notification.permission === "granted") {
189
+ console.log("Permission to receive notifications has been granted");
190
+ }
191
+
192
+ // Otherwise, we need to ask the user for permission
193
+ else if (Notification.permission !== 'denied') {
194
+ Notification.requestPermission(function (permission) {
195
+ // If the user accepts, let's create a notification
196
+ if (permission === "granted") {
197
+ console.log("Permission to receive notifications has been granted");
198
+ }
199
+ });
200
+ }
201
+ ```
202
+
203
+ If everything worked, you should see a desktop notification triggered via web
204
+ push. Yay!
205
+
206
+ Note: if you're using Rails, check out [serviceworker-rails](https://github.com/rossta/serviceworker-rails), a gem that makes it easier to host serviceworker scripts and manifest.json files at canonical endpoints (i.e., non-digested URLs) while taking advantage of the asset pipeline.
207
+
208
+ ## API
209
+
210
+ ### With a payload
30
211
 
31
212
  ```ruby
32
213
  message = {
@@ -36,22 +217,25 @@ message = {
36
217
  }
37
218
 
38
219
  Webpush.payload_send(
39
- endpoint: "https://android.googleapis.com/gcm/send/eah7hak....",
220
+ endpoint: "https://fcm.googleapis.com/gcm/send/eah7hak....",
40
221
  message: JSON.generate(message),
41
222
  p256dh: "BO/aG9nYXNkZmFkc2ZmZHNmYWRzZmFl...",
42
223
  auth: "aW1hcmthcmFpa3V6ZQ==",
43
- ttl: 600, #optional, ttl in seconds, defaults to 2419200 (4 weeks)
44
- api_key: "[GoogleDeveloper APIKEY]" # optional, not used in Firefox.
224
+ ttl: 600 #optional, ttl in seconds, defaults to 2419200 (4 weeks),
225
+ vapid: {
226
+ subject: "mailto:sender@example.com",
227
+ public_key: ENV['VAPID_PUBLIC_KEY'],
228
+ private_key: ENV['VAPID_PRIVATE_KEY']
229
+ }
45
230
  )
46
231
  ```
47
232
 
48
- ### not use the payload
233
+ ### Without a payload
49
234
 
50
235
  ```ruby
51
236
  Webpush.payload_send(
52
- endpoint: "https://android.googleapis.com/gcm/send/eah7hak....",
53
- ttl: 600, #optional, ttl in seconds, defaults to 2419200 (4 weeks)
54
- api_key: "[GoogleDeveloper APIKEY]" # optional, not used in Firefox.
237
+ endpoint: "https://fcm.googleapis.com/gcm/send/eah7hak....",
238
+ ttl: 600 #optional, ttl in seconds, defaults to 2419200 (4 weeks)
55
239
  )
56
240
  ```
57
241
 
data/lib/webpush.rb CHANGED
@@ -5,15 +5,12 @@ require 'net/http'
5
5
  require 'json'
6
6
 
7
7
  require 'webpush/version'
8
+ require 'webpush/errors'
9
+ require 'webpush/vapid_key'
8
10
  require 'webpush/encryption'
9
11
  require 'webpush/request'
10
12
 
11
13
  module Webpush
12
-
13
- # It is temporary URL until supported by the GCM server.
14
- GCM_URL = 'https://android.googleapis.com/gcm/send'
15
- TEMP_GCM_URL = 'https://gcm-http.googleapis.com/gcm'
16
-
17
14
  class << self
18
15
  # Deliver the payload to the required endpoint given by the JavaScript
19
16
  # PushSubscription. Including an optional message requires p256dh and
@@ -23,23 +20,32 @@ module Webpush
23
20
  # @param message [String] the optional payload
24
21
  # @param p256dh [String] the user's public ECDH key given by the PushSubscription
25
22
  # @param auth [String] the user's private ECDH key given by the PushSubscription
23
+ # @param vapid [Hash<Symbol,String>] options for VAPID
24
+ # @option vapid [String] :subject contact URI for the app server as a "mailto:" or an "https:"
25
+ # @option vapid [String] :public_key the VAPID public key
26
+ # @option vapid [String] :private_key the VAPID private key
26
27
  # @param options [Hash<Symbol,String>] additional options for the notification
27
- # @option options [String] :api_key required for Google, omit for Firefox
28
28
  # @option options [#to_s] :ttl Time-to-live in seconds
29
- def payload_send(endpoint:, message: "", p256dh: "", auth: "", **options)
30
- endpoint = endpoint.gsub(GCM_URL, TEMP_GCM_URL)
31
-
32
- payload = build_payload(message, p256dh, auth)
33
-
34
- Webpush::Request.new(endpoint, options.merge(payload: payload)).perform
29
+ def payload_send(message: "", endpoint:, p256dh: "", auth: "", vapid: {}, **options)
30
+ subscription = {
31
+ endpoint: endpoint,
32
+ keys: {
33
+ p256dh: p256dh,
34
+ auth: auth
35
+ }
36
+ }
37
+ Webpush::Request.new(
38
+ message: message,
39
+ subscription: subscription,
40
+ vapid: vapid,
41
+ **options
42
+ ).perform
35
43
  end
36
44
 
37
- private
38
-
39
- def build_payload(message, p256dh, auth)
40
- return {} if message.nil? || message.empty?
41
-
42
- Webpush::Encryption.encrypt(message, p256dh, auth)
45
+ # public_key: vapid_key.public_key.to_bn.to_s(2)
46
+ # private_key: vapid_key.private_key.to_s(2)
47
+ def generate_key
48
+ VapidKey.new
43
49
  end
44
50
  end
45
51
  end
@@ -36,6 +36,7 @@ module Webpush
36
36
  ciphertext: ciphertext,
37
37
  salt: salt,
38
38
  server_public_key_bn: convert16bit(server_public_key_bn),
39
+ server_public_key: server_public_key_bn.to_s(2),
39
40
  shared_secret: shared_secret
40
41
  }
41
42
  end
@@ -0,0 +1,9 @@
1
+ module Webpush
2
+ class Error < RuntimeError; end
3
+
4
+ class ConfigurationError < Error; end
5
+
6
+ class ResponseError < Error; end
7
+
8
+ class InvalidSubscription < ResponseError; end
9
+ end
@@ -1,20 +1,21 @@
1
- module Webpush
2
-
3
- class ResponseError < RuntimeError
4
- end
1
+ require 'jwt'
2
+ require 'base64'
5
3
 
6
- class InvalidSubscription < ResponseError
7
- end
4
+ module Webpush
5
+ # It is temporary URL until supported by the GCM server.
6
+ GCM_URL = 'https://android.googleapis.com/gcm/send'
7
+ TEMP_GCM_URL = 'https://gcm-http.googleapis.com/gcm'
8
8
 
9
9
  class Request
10
- def initialize(endpoint, options = {})
11
- @endpoint = endpoint
10
+ def initialize(message: "", subscription:, vapid:, **options)
11
+ endpoint = subscription.fetch(:endpoint)
12
+ @endpoint = endpoint.gsub(GCM_URL, TEMP_GCM_URL)
13
+ @payload = build_payload(message, subscription)
14
+ @vapid_options = vapid
12
15
  @options = default_options.merge(options)
13
- @payload = @options.delete(:payload) || {}
14
16
  end
15
17
 
16
18
  def perform
17
- uri = URI.parse(@endpoint)
18
19
  http = Net::HTTP.new(uri.host, uri.port)
19
20
  http.use_ssl = true
20
21
  req = Net::HTTP::Post.new(uri.request_uri, headers)
@@ -36,52 +37,114 @@ module Webpush
36
37
  headers["Content-Type"] = "application/octet-stream"
37
38
  headers["Ttl"] = ttl
38
39
 
39
- if encrypted_payload?
40
+ if @payload.has_key?(:server_public_key)
40
41
  headers["Content-Encoding"] = "aesgcm"
41
42
  headers["Encryption"] = "salt=#{salt_param}"
42
43
  headers["Crypto-Key"] = "dh=#{dh_param}"
43
44
  end
44
45
 
45
- headers["Authorization"] = "key=#{api_key}" if api_key?
46
+ if api_key?
47
+ headers["Authorization"] = api_key
48
+ elsif vapid?
49
+ vapid_headers = build_vapid_headers
50
+ headers["Authorization"] = vapid_headers["Authorization"]
51
+ headers["Crypto-Key"] = [ headers["Crypto-Key"], vapid_headers["Crypto-Key"] ].compact.join(";")
52
+ end
46
53
 
47
54
  headers
48
55
  end
49
56
 
57
+ def build_vapid_headers
58
+ vapid_key = VapidKey.from_keys(vapid_public_key, vapid_private_key)
59
+ jwt = JWT.encode(jwt_payload, vapid_key.curve, 'ES256')
60
+ p256ecdsa = vapid_key.public_key_for_push_header
61
+
62
+ {
63
+ 'Authorization' => 'WebPush ' + jwt,
64
+ 'Crypto-Key' => 'p256ecdsa=' + p256ecdsa,
65
+ }
66
+ end
67
+
50
68
  def body
51
69
  @payload.fetch(:ciphertext, "")
52
70
  end
53
71
 
54
72
  private
55
73
 
74
+ def uri
75
+ @uri ||= URI.parse(@endpoint)
76
+ end
77
+
56
78
  def ttl
57
79
  @options.fetch(:ttl).to_s
58
80
  end
59
81
 
60
- def api_key
61
- @options.fetch(:api_key, nil)
82
+ def dh_param
83
+ trim_encode64(@payload.fetch(:server_public_key))
62
84
  end
63
85
 
64
- def api_key?
65
- !(api_key.nil? || api_key.empty?) && @endpoint =~ /\Ahttps:\/\/(android|gcm-http)\.googleapis\.com/
86
+ def salt_param
87
+ trim_encode64(@payload.fetch(:salt))
66
88
  end
67
89
 
68
- def encrypted_payload?
69
- [:ciphertext, :server_public_key_bn, :salt].all? { |key| @payload.has_key?(key) }
90
+ def jwt_payload
91
+ {
92
+ aud: audience,
93
+ exp: Time.now.to_i + expiration,
94
+ sub: subject,
95
+ }
70
96
  end
71
97
 
72
- def dh_param
73
- Base64.urlsafe_encode64(@payload.fetch(:server_public_key_bn)).delete('=')
98
+ def audience
99
+ uri.scheme + "://" + uri.host
74
100
  end
75
101
 
76
- def salt_param
77
- Base64.urlsafe_encode64(@payload.fetch(:salt)).delete('=')
102
+ def expiration
103
+ @vapid_options.fetch(:expiration, 24*60*60)
104
+ end
105
+
106
+ def subject
107
+ @vapid_options.fetch(:subject, 'sender@example.com')
108
+ end
109
+
110
+ def vapid_public_key
111
+ @vapid_options.fetch(:public_key, nil)
112
+ end
113
+
114
+ def vapid_private_key
115
+ @vapid_options.fetch(:private_key, nil)
78
116
  end
79
117
 
80
118
  def default_options
81
119
  {
82
- api_key: nil,
83
120
  ttl: 60*60*24*7*4 # 4 weeks
84
121
  }
85
122
  end
123
+
124
+ def build_payload(message, subscription)
125
+ return {} if message.nil? || message.empty?
126
+
127
+ encrypt_payload(message, subscription.fetch(:keys))
128
+ end
129
+
130
+ def encrypt_payload(message, p256dh:, auth:)
131
+ Encryption.encrypt(message, p256dh, auth)
132
+ end
133
+
134
+ def api_key
135
+ @options.fetch(:api_key, nil)
136
+ end
137
+
138
+ def api_key?
139
+ !(api_key.nil? || api_key.empty?) && @endpoint =~ /\Ahttps:\/\/(android|gcm-http)\.googleapis\.com/
140
+ end
141
+
142
+ def vapid?
143
+ @vapid_options.any?
144
+ end
145
+
146
+ def trim_encode64(bin)
147
+ Base64.urlsafe_encode64(bin).delete('=')
148
+ end
86
149
  end
87
150
  end
@@ -0,0 +1,75 @@
1
+ module Webpush
2
+ class VapidKey
3
+ def self.from_keys(public_key, private_key)
4
+ key = new
5
+ key.public_key = public_key
6
+ key.private_key = private_key
7
+
8
+ key
9
+ end
10
+
11
+ attr_reader :curve
12
+
13
+ def initialize
14
+ @curve = OpenSSL::PKey::EC.new('prime256v1')
15
+ @curve.generate_key
16
+ end
17
+
18
+ # Retrieve the encoded EC public key for server-side storage
19
+ # @return encoded binary representaion of 65-byte VAPID public key
20
+ def public_key
21
+ encode64(curve.public_key.to_bn.to_s(2))
22
+ end
23
+
24
+ # Retrieve EC public key for Web Push
25
+ # @return the encoded VAPID public key suitable for Web Push transport
26
+ def public_key_for_push_header
27
+ trim_encode64(curve.public_key.to_bn.to_s(2))
28
+ end
29
+
30
+ # Convenience
31
+ # @return base64 urlsafe-encoded binary representaion of 32-byte VAPID private key
32
+ def private_key
33
+ Base64.urlsafe_encode64(curve.private_key.to_s(2))
34
+ end
35
+
36
+ def public_key=(key)
37
+ @curve.public_key = OpenSSL::PKey::EC::Point.new(group, to_big_num(key))
38
+ end
39
+
40
+ def private_key=(key)
41
+ @curve.private_key = to_big_num(key)
42
+ end
43
+
44
+ def curve_name
45
+ group.curve_name
46
+ end
47
+
48
+ def group
49
+ curve.group
50
+ end
51
+
52
+ def to_h
53
+ { public_key: public_key, private_key: private_key }
54
+ end
55
+ alias to_hash to_h
56
+
57
+ def inspect
58
+ "#<#{self.class}:#{object_id.to_s(16)} #{to_h.map { |k, v| ":#{k}=#{v}" }.join(" ")}>"
59
+ end
60
+
61
+ private
62
+
63
+ def to_big_num(key)
64
+ OpenSSL::BN.new(Base64.urlsafe_decode64(key), 2)
65
+ end
66
+
67
+ def encode64(bin)
68
+ Base64.urlsafe_encode64(bin)
69
+ end
70
+
71
+ def trim_encode64(bin)
72
+ encode64(bin).delete('=')
73
+ end
74
+ end
75
+ end
@@ -1,3 +1,3 @@
1
1
  module Webpush
2
- VERSION = "0.2.5"
2
+ VERSION = "0.3.0"
3
3
  end
data/webpush.gemspec CHANGED
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ["lib"]
19
19
 
20
20
  spec.add_dependency "hkdf", "~> 0.2"
21
+ spec.add_dependency "jwt"
21
22
 
22
23
  spec.add_development_dependency "bundler", "~> 1.11"
23
24
  spec.add_development_dependency 'pry'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: webpush
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - zaru@sakuraba
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-09-14 00:00:00.000000000 Z
11
+ date: 2016-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hkdf
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: jwt
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -119,6 +133,7 @@ files:
119
133
  - ".rspec"
120
134
  - ".travis.yml"
121
135
  - Gemfile
136
+ - LICENSE
122
137
  - README.md
123
138
  - Rakefile
124
139
  - bin/console
@@ -127,7 +142,9 @@ files:
127
142
  - bin/setup
128
143
  - lib/webpush.rb
129
144
  - lib/webpush/encryption.rb
145
+ - lib/webpush/errors.rb
130
146
  - lib/webpush/request.rb
147
+ - lib/webpush/vapid_key.rb
131
148
  - lib/webpush/version.rb
132
149
  - webpush.gemspec
133
150
  homepage: https://github.com/zaru/webpush
@@ -149,7 +166,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
166
  version: '0'
150
167
  requirements: []
151
168
  rubyforge_project:
152
- rubygems_version: 2.4.5.1
169
+ rubygems_version: 2.5.1
153
170
  signing_key:
154
171
  specification_version: 4
155
172
  summary: Encryption Utilities for Web Push payload.