Twitch Webhook Fixes (#2692)

* Twitch Webhook Registration Fix

This fixes the broken twitch webhook registration. I previously added
the `to_json` when I was switching to using HTTParty and working through
WebMock. I apparently forgot to test this manually however after and
relied on the specs that had some baked in bad assumptions.

This removes the `to_json` and configures the mocking properly in the
spec setup.

I tested locally that this can now succesfully register for webhooks
with Twitch!

* Fix Webhook verification digesting as well, hardcode the specs for durability

* Refactor to use shorter digest method

* Add cache buster lines from Bens branch
This commit is contained in:
Corey Alexander 2019-05-05 11:52:43 -04:00 committed by Ben Halpern
parent fa3f4dcd0f
commit 23ded947e3
5 changed files with 31 additions and 61 deletions

View file

@ -35,10 +35,8 @@ class TwitchStreamUpdatesController < ApplicationController
def secret_verified?
twitch_sha = request.headers["x-hub-signature"]
digest = Digest::SHA256.new
digest << ApplicationConfig["TWITCH_WEBHOOK_SECRET"]
digest << request.raw_post
digest = OpenSSL::HMAC.hexdigest("SHA256", ApplicationConfig["TWITCH_WEBHOOK_SECRET"], request.raw_post)
twitch_sha == "sha256=#{digest.hexdigest}"
twitch_sha == "sha256=#{digest}"
end
end

View file

@ -504,6 +504,9 @@ class User < ApplicationRecord
def bust_cache
CacheBuster.new.bust("/#{username}")
CacheBuster.new.bust("/#{username}?i=i")
CacheBuster.new.bust("/live/#{username}")
CacheBuster.new.bust("/live/#{username}?i=i")
CacheBuster.new.bust("/feed/#{username}")
end
handle_asynchronously :bust_cache

View file

@ -34,7 +34,7 @@ module Streams
"hub.lease_seconds" => WEBHOOK_LEASE_SECONDS,
"hub.topic" => "https://api.twitch.tv/helix/streams?user_id=#{twitch_user_id}",
"hub.secret" => ApplicationConfig["TWITCH_WEBHOOK_SECRET"]
}.to_json
}
end
def authentication_request_headers

View file

@ -41,35 +41,23 @@ RSpec.describe "TwitchStramUpdates", type: :request do
end
describe "POST /users/:user_id/twitch_stream_updates" do
before do
allow(ApplicationConfig).to receive(:[]).and_call_original
allow(ApplicationConfig).to receive(:[]).with("TWITCH_WEBHOOK_SECRET").and_return("FAKE_TWITCH_WEBHOOK_SECRET")
end
context "when the user was not streaming and starts streaming" do
let(:currently_streaming_on) { nil }
let(:twitch_webhook_params) do
{
data: [{
id: "0123456789",
user_id: "5678",
user_name: "wjdtkdqhs",
game_id: "21779",
community_ids: [],
type: "live",
title: "Best Stream Ever",
viewer_count: 417,
started_at: "2017-12-01T10:09:45Z",
language: "en",
thumbnail_url: "https://link/to/thumbnail.jpg"
}]
}
'{"data":[{"id":"0123456789","user_id":"5678","user_name":"wjdtkdqhs","game_id":"21779","community_ids":[],"type":"live","title":"Best Stream Ever","viewer_count":417,"started_at":"2017-12-01T10:09:45Z","language":"en","thumbnail_url":"https://link/to/thumbnail.jpg"}]}'
end
let(:twitch_webhook_secret_sha) do
digest = Digest::SHA256.new
digest << ApplicationConfig["TWITCH_WEBHOOK_SECRET"]
digest << twitch_webhook_params.to_json
"sha256=#{digest.hexdigest}"
"sha256=96840ed4d83666551e43b384d94ac481367504564d6474b5dea710cedde5ce18"
end
it "updates the Users twitch streaming status" do
expect { post "/users/#{user.id}/twitch_stream_updates", params: twitch_webhook_params.to_json, headers: { "Content-Type" => "application/json", "X-Hub-Signature" => twitch_webhook_secret_sha } }.
expect { post "/users/#{user.id}/twitch_stream_updates", params: twitch_webhook_params, headers: { "Content-Type" => "application/json", "X-Hub-Signature" => twitch_webhook_secret_sha } }.
to change { user.reload.currently_streaming? }.from(false).to(true).
and change { user.reload.currently_streaming_on_twitch? }.from(false).to(true)
end
@ -77,51 +65,32 @@ RSpec.describe "TwitchStramUpdates", type: :request do
context "when the webhook secret was NOT verified" do
let(:twitch_webhook_params) do
{
data: [{
id: "0123456789",
user_id: "5678",
user_name: "wjdtkdqhs",
game_id: "21779",
community_ids: [],
type: "live",
title: "Best Stream Ever",
viewer_count: 417,
started_at: "2017-12-01T10:09:45Z",
language: "en",
thumbnail_url: "https://link/to/thumbnail.jpg"
}]
}
'{"data":[{"id":"0123456789","user_id":"5678","user_name":"wjdtkdqhs","game_id":"21779","community_ids":[],"type":"live","title":"Best Stream Ever","viewer_count":417,"started_at":"2017-12-01T10:09:45Z","language":"en","thumbnail_url":"https://link/to/thumbnail.jpg"}]}'
end
let(:twitch_webhook_secret_sha) { "sha256=BAD_HASH" }
it "noops" do
expect { post "/users/#{user.id}/twitch_stream_updates", params: twitch_webhook_params.to_json, headers: { "Content-Type" => "application/json", "X-Hub-Signature" => twitch_webhook_secret_sha } }.
expect { post "/users/#{user.id}/twitch_stream_updates", params: twitch_webhook_params, headers: { "Content-Type" => "application/json", "X-Hub-Signature" => twitch_webhook_secret_sha } }.
to not_change { user.reload.currently_streaming? }.from(false).
and not_change { user.reload.currently_streaming_on_twitch? }.from(false)
end
end
end
context "when the user was streaming and stops" do
let(:currently_streaming_on) { :twitch }
context "when the user was streaming and stops" do
let(:currently_streaming_on) { :twitch }
let(:twitch_webhook_params) do
{
data: []
}
end
let(:twitch_webhook_secret_sha) do
digest = Digest::SHA256.new
digest << ApplicationConfig["TWITCH_WEBHOOK_SECRET"]
digest << twitch_webhook_params.to_json
"sha256=#{digest.hexdigest}"
end
let(:twitch_webhook_params) do
'{"data":[]}'
end
let(:twitch_webhook_secret_sha) do
"sha256=3222cdd929339a296712edf3ec820266b1e29c0e4eeb715dd18d98c0c2294ca3"
end
it "updates the Users twitch streaming status" do
expect { post "/users/#{user.id}/twitch_stream_updates", params: twitch_webhook_params.to_json, headers: { "Content-Type" => "application/json", "X-Hub-Signature" => twitch_webhook_secret_sha } }.
to change { user.reload.currently_streaming? }.from(true).to(false).
and change { user.reload.currently_streaming_on_twitch? }.from(true).to(false)
it "updates the Users twitch streaming status" do
expect { post "/users/#{user.id}/twitch_stream_updates", params: twitch_webhook_params, headers: { "Content-Type" => "application/json", "X-Hub-Signature" => twitch_webhook_secret_sha } }.
to change { user.reload.currently_streaming? }.from(true).to(false).
and change { user.reload.currently_streaming_on_twitch? }.from(true).to(false)
end
end
end
end

View file

@ -15,11 +15,11 @@ RSpec.describe Streams::TwitchWebhook::Register, type: :service do
"hub.lease_seconds" => 604_800,
"hub.topic" => "https://api.twitch.tv/helix/streams?user_id=654321",
"hub.secret" => ApplicationConfig["TWITCH_WEBHOOK_SECRET"]
}.to_json
}
end
let!(:twitch_webhook_registration_stubbed_route) do
stub_request(:post, "https://api.twitch.tv/helix/webhooks/hub").
with(body: expected_twitch_webhook_params, headers: expected_headers).
with(body: URI.encode_www_form(expected_twitch_webhook_params), headers: expected_headers).
and_return(status: 204)
end