messagebird-rest 3.0.0 → 3.1.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
  SHA256:
3
- metadata.gz: 9dc78fcfe4e496c197244a6e62ad347bc439c52334d69a453f25977f7981c23b
4
- data.tar.gz: 6cef4d63e95ddd4455dd4a003b60bdfc3dfb1b10cfc1049cee9aecc7d8d5fb41
3
+ metadata.gz: f31ec7bbc1d49554a915dde71c24245246453df62bfd7ab7a4b912727990de5b
4
+ data.tar.gz: 9dcdd828d54250134e2d4cd95a3ccd418d799e7f1e387324885faa8b2d23e385
5
5
  SHA512:
6
- metadata.gz: f235d92c2c49b14c7529c3aa2ad5e8fb6e03adf66a74f4d55229b1b262c66d8beca1a9137ff1d24da439a8d9051915765c7371e17fc1eae63bc0e56e8b96f715
7
- data.tar.gz: 4b264d192ab011f784aedd9f336e750a1d20b9413d6cb9fd2e67d37cef6b36f7ceed25b80d6dbb74e90b4ffb452f90939b3343a732d2043138071a2b15f6260e
6
+ metadata.gz: e6c773c63307706d30dd415dae6860714575a0ed4ce217abec279acc1030120899b38871c4e62d7b7cd6d207ac16049c519d2c81a05d6a9cbdae60b69436a5c1
7
+ data.tar.gz: 1595298d447d97acfb71ee8dfd3cef0e9facab22935bb30abe7d000fed73919c34b548340b93d087cea98393ec4ebafe2d3d2c7f529fb72f01b0b62d2e498663
data/README.md CHANGED
@@ -2,7 +2,7 @@ MessageBird's REST API for Ruby
2
2
  ===============================
3
3
  This repository contains the open source Ruby client for MessageBird's REST API. Documentation can be found at: https://developers.messagebird.com/
4
4
 
5
- [![Build Status](https://travis-ci.org/messagebird/ruby-rest-api.svg?branch=master)](https://travis-ci.org/messagebird/ruby-rest-api)
5
+ [![Build Status](https://github.com/messagebird/ruby-rest-api/actions/workflows/ruby_ci.yml/badge.svg)](https://github.com/messagebird/ruby-rest-api/actions)
6
6
 
7
7
  Requirements
8
8
  ------------
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+ require 'digest'
5
+ require 'time'
6
+ require 'jwt'
7
+
8
+ module MessageBird
9
+ class ValidationError < StandardError
10
+ end
11
+
12
+ ##
13
+ # RequestValidator validates request signature signed by MessageBird services.
14
+ #
15
+ # @see https://developers.messagebird.com/docs/verify-http-requests
16
+ class RequestValidator
17
+ ALLOWED_ALGOS = %w[HS256 HS384 HS512].freeze
18
+
19
+ ##
20
+ #
21
+ # @param [string] signature_key customer signature key. Can be retrieved through <a href="https://dashboard.messagebird.com/developers/settings">Developer Settings</a>. This is NOT your API key.
22
+ # @param [bool] skip_url_validation whether url_hash claim validation should be skipped. Note that when true, no query parameters should be trusted.
23
+ def initialize(signature_key, skip_url_validation = false)
24
+ @signature_key = signature_key
25
+ @skip_url_validation = skip_url_validation
26
+ end
27
+
28
+ ##
29
+ # This method validates provided request signature, which is a JWT token.
30
+ # This JWT is signed with a MessageBird account unique secret key, ensuring the request is from MessageBird and a specific account.
31
+ # The JWT contains the following claims:
32
+ # * "url_hash" - the raw URL hashed with SHA256 ensuring the URL wasn't altered.
33
+ # * "payload_hash" - the raw payload hashed with SHA256 ensuring the payload wasn't altered.
34
+ # * "jti" - a unique token ID to implement an optional non-replay check (NOT validated by default).
35
+ # * "nbf" - the not before timestamp.
36
+ # * "exp" - the expiration timestamp is ensuring that a request isn't captured and used at a later time.
37
+ # * "iss" - the issuer name, always MessageBird.
38
+ # @param [String] signature the actual signature taken from request header "MessageBird-Signature-JWT".
39
+ # @param [String] url the raw url including the protocol, hostname and query string, e.g. "https://example.com/?example=42".
40
+ # @param [Array] request_body the raw request body.
41
+ # @return [Array] raw signature payload
42
+ # @raise [ValidationError] if signature is invalid
43
+ # @see https://developers.messagebird.com/docs/verify-http-requests
44
+ def validate_signature(signature, url, request_body)
45
+ raise ValidationError, 'Signature can not be empty' if signature.to_s.empty?
46
+ raise ValidationError, 'URL can not be empty' if !@skip_url_validation && url.to_s.empty?
47
+
48
+ claims = decode_signature signature
49
+ validate_url(url, claims['url_hash']) unless @skip_url_validation
50
+ validate_payload(request_body, claims['payload_hash'])
51
+
52
+ claims
53
+ end
54
+
55
+ private # Applies to every method below this line
56
+
57
+ def decode_signature(signature)
58
+ begin
59
+ claims, * = JWT.decode signature, @signature_key, true,
60
+ algorithm: ALLOWED_ALGOS,
61
+ iss: 'MessageBird',
62
+ required_claims: %w[iss nbf exp],
63
+ verify_iss: true,
64
+ leeway: 1
65
+ rescue JWT::DecodeError => e
66
+ raise ValidationError, e
67
+ end
68
+
69
+ claims
70
+ end
71
+
72
+ def validate_url(url, url_hash)
73
+ expected_url_hash = Digest::SHA256.hexdigest url
74
+ unless JWT::SecurityUtils.secure_compare(expected_url_hash, url_hash)
75
+ raise ValidationError, 'invalid jwt: claim url_hash is invalid'
76
+ end
77
+ end
78
+
79
+ def validate_payload(body, payload_hash)
80
+ if !body.to_s.empty? && !payload_hash.to_s.empty?
81
+ unless JWT::SecurityUtils.secure_compare(Digest::SHA256.hexdigest(body), payload_hash)
82
+ raise ValidationError, 'invalid jwt: claim payload_hash is invalid'
83
+ end
84
+ elsif !body.to_s.empty?
85
+ raise ValidationError, 'invalid jwt: claim payload_hash is not set but payload is present'
86
+ elsif !payload_hash.to_s.empty?
87
+ raise ValidationError, 'invalid jwt: claim payload_hash is set but actual payload is missing'
88
+ end
89
+ end
90
+ end
91
+ end
@@ -5,10 +5,16 @@ require 'digest'
5
5
  require 'time'
6
6
 
7
7
  module MessageBird
8
+ ##
9
+ # @deprecated Use {MessageBird::RequestValidator::ValidationError} instead.
8
10
  class ValidationException < TypeError
9
11
  end
10
12
 
13
+ ##
14
+ # @deprecated Use {MessageBird::RequestValidator} instead.
11
15
  class SignedRequest
16
+ ##
17
+ # @deprecated Use {MessageBird::RequestValidator} instead.
12
18
  def initialize(query_parameters, signature, request_timestamp, body)
13
19
  unless query_parameters.is_a? Hash
14
20
  raise ValidationException, 'The "query_parameters" value is invalid.'
@@ -29,12 +35,16 @@ module MessageBird
29
35
  @body = body
30
36
  end
31
37
 
38
+ ##
39
+ # @deprecated Use {MessageBird::RequestValidator::validateSignature} instead.
32
40
  def verify(signing_key)
33
41
  calculated_signature = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), signing_key, build_payload)
34
42
  expected_signature = Base64.decode64(@signature)
35
43
  calculated_signature.bytes == expected_signature.bytes
36
44
  end
37
45
 
46
+ ##
47
+ # @deprecated Use {MessageBird::RequestValidator} instead.
38
48
  def build_payload
39
49
  parts = []
40
50
  parts.push(@request_timestamp)
@@ -43,6 +53,8 @@ module MessageBird
43
53
  parts.join("\n")
44
54
  end
45
55
 
56
+ ##
57
+ # @deprecated Use {MessageBird::RequestValidator} instead.
46
58
  def recent?(offset = 10)
47
59
  (Time.now.getutc.to_i - @request_timestamp) < offset
48
60
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module MessageBird
4
4
  module Version
5
- STRING = '3.0.0'
5
+ STRING = '3.1.0'
6
6
  end
7
7
  end
data/lib/messagebird.rb CHANGED
@@ -12,7 +12,8 @@ require 'messagebird/group_reference'
12
12
  require 'messagebird/hlr'
13
13
  require 'messagebird/http_client'
14
14
  require 'messagebird/message_reference'
15
- require 'messagebird/signed_request'
15
+ require 'messagebird/signed_request' # @deprecated
16
+ require 'messagebird/request_validator'
16
17
  require 'messagebird/verify'
17
18
  require 'messagebird/message'
18
19
  require 'messagebird/voicemessage'
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: messagebird-rest
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maurice Nonnekes
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-25 00:00:00.000000000 Z
11
+ date: 2022-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jwt
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.2.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.2.3
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rspec
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -87,6 +101,7 @@ files:
87
101
  - lib/messagebird/number.rb
88
102
  - lib/messagebird/number_client.rb
89
103
  - lib/messagebird/recipient.rb
104
+ - lib/messagebird/request_validator.rb
90
105
  - lib/messagebird/signed_request.rb
91
106
  - lib/messagebird/verify.rb
92
107
  - lib/messagebird/verify_email_message.rb
@@ -105,7 +120,7 @@ homepage: https://github.com/messagebird/ruby-rest-api
105
120
  licenses:
106
121
  - BSD-2-Clause
107
122
  metadata: {}
108
- post_install_message:
123
+ post_install_message:
109
124
  rdoc_options: []
110
125
  require_paths:
111
126
  - lib
@@ -120,8 +135,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
135
  - !ruby/object:Gem::Version
121
136
  version: '0'
122
137
  requirements: []
123
- rubygems_version: 3.2.15
124
- signing_key:
138
+ rubygems_version: 3.1.2
139
+ signing_key:
125
140
  specification_version: 4
126
141
  summary: MessageBird's REST API
127
142
  test_files: []