webpush 0.2.5 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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.