class WebPush

Constants

DEFAULT_EXP
DEFAULT_TTL
GROUP_NAME
VERSION

Public Class Methods

new(subscription, ttl: DEFAULT_TTL, exp: DEFAULT_EXP) click to toggle source
# File lib/web_push.rb, line 26
def initialize(subscription, ttl: DEFAULT_TTL, exp: DEFAULT_EXP)
  @subscription = subscription
  @endpoint = @subscription[:endpoint]
  @p256dh   = @subscription[:keys][:p256dh]
  @auth     = @subscription[:keys][:auth]
  @ttl = ttl
  @exp = exp
end

Public Instance Methods

encrypt(p256dh, auth, payload) click to toggle source
# File lib/web_push.rb, line 72
def encrypt(p256dh, auth, payload)
  user_public_key = urlsafe_decode64 p256dh
  user_auth = urlsafe_decode64 auth

  local_curve = Utils.generate_vapid_pkey
  user_public_key_point = OpenSSL::PKey::EC::Point.new(local_curve.group, OpenSSL::BN.new(user_public_key, 2))

  key = local_curve.dh_compute_key(user_public_key_point)
  server_public_key = local_curve.public_key.to_bn.to_s(2)

  params = {
    key: key,
    salt: SecureRandom.random_bytes(16),
    server_public_key: server_public_key,
    user_public_key: user_public_key,
    auth: user_auth
  }
  params[:cipher] = ECE.encrypt(payload, params)

  params
end
generate_http_request(payload) click to toggle source
# File lib/web_push.rb, line 48
def generate_http_request(payload)
  {
    method: 'POST',
    headers: {
      'TTL' => @ttl.to_s,
      'Content-Type' => 'application/octet-stream',
      'Content-Encoding' => 'aesgcm',
    },
    body: nil,
    uri: URI(@endpoint),
  }.tap do |request|
    encrypted = encrypt(@p256dh, @auth, payload)
    request[:headers]['Content-Length'] = encrypted[:cipher].bytesize.to_s
    request[:headers]['Encryption'] = 'salt=' + urlsafe_encode64(encrypted[:salt])
    request[:headers]['Crypto-Key'] = 'dh=' + urlsafe_encode64(encrypted[:server_public_key])
    request[:body] = encrypted[:cipher]

    audience = request[:uri].scheme + "://" + request[:uri].host
    vapid_headers = generate_vapid_headers audience
    request[:headers]['Authorization'] = vapid_headers['Authorization']
    request[:headers]['Crypto-Key'] += ';' + vapid_headers['Crypto-Key']
  end
end
generate_vapid_headers(audience, sub: @vapid_subject, exp: @exp) click to toggle source
# File lib/web_push.rb, line 94
def generate_vapid_headers(audience, sub: @vapid_subject, exp: @exp)
  jwt = JWT.encode({
                     aud: audience,
                     exp: Time.now.to_i + exp,
                     sub: sub,
                   }, @vapid_pkey, 'ES256')
  {
    'Authorization' => 'WebPush ' + jwt,
    'Crypto-Key' => 'p256ecdsa=' + urlsafe_encode64(@vapid_public_key_bn),
  }
end
generate_vapid_pkey(public_key, private_key) click to toggle source
# File lib/web_push.rb, line 114
def generate_vapid_pkey(public_key, private_key)
  pvtbn = OpenSSL::BN.new(urlsafe_decode64(private_key), 2)
  pubbn = OpenSSL::BN.new(urlsafe_decode64(public_key), 2)

  pkey = Utils.generate_vapid_pkey
  pkey.private_key = pvtbn
  pkey.public_key = OpenSSL::PKey::EC::Point.new(pkey.group, pubbn)

  pkey
end
send_notification(payload) click to toggle source
# File lib/web_push.rb, line 35
def send_notification(payload)
  params = generate_http_request(payload)

  uri = params[:uri]
  https = Net::HTTP.new uri.host, uri.port
  https.use_ssl = true

  request = Net::HTTP::Post.new uri.request_uri, params[:headers]
  request.body = params[:body]

  https.request request
end
set_vapid_details(subject, public_key, private_key) click to toggle source
# File lib/web_push.rb, line 106
def set_vapid_details(subject, public_key, private_key)
  @vapid_subject = subject
  @vapid_public_key = public_key
  @vapid_private_key = private_key
  @vapid_pkey = generate_vapid_pkey(@vapid_public_key, @vapid_private_key)
  @vapid_public_key_bn = @vapid_pkey.public_key.to_bn.to_s(2)
end
urlsafe_decode64(bin) click to toggle source
# File lib/web_push.rb, line 129
def urlsafe_decode64(bin)
  # take from ruby v2.3.3 base64.rb
  if !bin.end_with?("=") && bin.length % 4 != 0
    Base64.urlsafe_decode64 bin.ljust((bin.length + 3) & ~3, "=")
  else
    Base64.urlsafe_decode64 bin
  end
end
urlsafe_encode64(bin) click to toggle source
# File lib/web_push.rb, line 125
def urlsafe_encode64(bin)
  Base64.urlsafe_encode64(bin).delete('=')
end