Skip to content

Commit

Permalink
Add support for specifying signing secrets on a per-request basis (#256)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gabriel Deal authored and dblock committed Mar 5, 2019
1 parent d0c1b75 commit a9dabc5
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
@@ -1,5 +1,6 @@
### 0.14.2 (Next)

* [#256](https://github.com/slack-ruby/slack-ruby-client/pull/256): Added support for specifying signing secrets on a per-request basis via optional parameters to the `Slack::Events::Request` constructor - [@gabrielmdeal](https://github.com/gabrielmdeal).
* Your contribution here.

### 0.14.1 (2019/2/26)
Expand Down
7 changes: 7 additions & 0 deletions README.md
Expand Up @@ -506,6 +506,13 @@ slack_request = Slack::Events::Request.new(http_request)
slack_request.verify!
```

To specify secrets on a per-request basis:
```ruby
Slack::Events::Request.new(http_request,
signing_secret: signing_secret,
signature_expires_in: signature_expires_in)
```

The `verify!` call may raise `Slack::Events::MissingSigningSecret`, `Slack::Events::InvalidSignature` or `Slack::Events::TimestampExpired` errors.

### Message Parsing
Expand Down
14 changes: 9 additions & 5 deletions lib/slack/events/request.rb
Expand Up @@ -5,10 +5,14 @@ class MissingSigningSecret < StandardError; end
class TimestampExpired < StandardError; end
class InvalidSignature < StandardError; end

attr_reader :http_request
attr_reader :http_request,
:signing_secret,
:signature_expires_in

def initialize(http_request)
def initialize(http_request, options = {})
@http_request = http_request
@signing_secret = options[:signing_secret] || Slack::Events.config.signing_secret
@signature_expires_in = options[:signature_expires_in] || Slack::Events.config.signature_expires_in
end

# Request timestamp.
Expand All @@ -34,16 +38,16 @@ def body

# Returns true if the signature coming from Slack has expired.
def expired?
timestamp.nil? || (Time.now.to_i - timestamp.to_i).abs > Slack::Events.config.signature_expires_in
timestamp.nil? || (Time.now.to_i - timestamp.to_i).abs > signature_expires_in
end

# Returns true if the signature coming from Slack is valid.
def valid?
raise MissingSigningSecret unless Slack::Events.config.signing_secret
raise MissingSigningSecret unless signing_secret

digest = OpenSSL::Digest::SHA256.new
signature_basestring = [version, timestamp, body].join(':')
hex_hash = OpenSSL::HMAC.hexdigest(digest, Slack::Events.config.signing_secret, signature_basestring)
hex_hash = OpenSSL::HMAC.hexdigest(digest, signing_secret, signature_basestring)
computed_signature = [version, hex_hash].join('=')
computed_signature == signature
end
Expand Down
30 changes: 29 additions & 1 deletion spec/slack/events/request_spec.rb
Expand Up @@ -3,10 +3,11 @@
RSpec.describe Slack::Events::Request do
before do
Slack::Events.configure do |config|
config.signing_secret = 'ade6ca762ade4db0e7d31484cd616b9c'
config.signing_secret = signing_secret
config.signature_expires_in = 30
end
end
let(:signing_secret) { 'ade6ca762ade4db0e7d31484cd616b9c' }
let(:signature) { 'v0=91177eea054d65de0fc0f9b4ec57714307bc0ce2c5f3bf0d28b1b720c8f92ba2' }
let(:timestamp) { '1547933148' }
let(:body) { '{"token":"X34FAqCu8tmGEkEEpoDncnja","challenge":"P7sFXA4o3HV2hTx4zb4zcQ9yrvuQs8pDh6EacOxmMRj0tJaXfQFF","type":"url_verification"}' }
Expand Down Expand Up @@ -115,6 +116,33 @@
end
end
end
context 'without global config' do
before do
Slack::Events.config.reset
end
context 'without a signing secret parameter' do
subject do
Slack::Events::Request.new(http_request)
end
it 'raises MissingSigningSecret' do
expect { subject.valid? }.to raise_error Slack::Events::Request::MissingSigningSecret
end
end
context 'with a signing secret parameter' do
subject do
Slack::Events::Request.new(http_request,
signing_secret: signing_secret,
signature_expires_in: 30)
end
before do
Timecop.freeze(Time.at(timestamp.to_i))
end
it 'is valid and not expired' do
expect(subject).to be_valid
expect(subject).to_not be_expired
end
end
end
after do
Slack::Events.config.reset
end
Expand Down

0 comments on commit a9dabc5

Please sign in to comment.