From 3a2f70fcd94294809b7a1521d8cf9011896d6bd7 Mon Sep 17 00:00:00 2001 From: Johann Wilfrid-Calixte Date: Tue, 4 Jul 2023 14:00:11 +0200 Subject: [PATCH 1/9] fix: Link the right section of the documentation for authenticate method --- lib/pusher/client.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pusher/client.rb b/lib/pusher/client.rb index bb7960b..df3c0cd 100644 --- a/lib/pusher/client.rb +++ b/lib/pusher/client.rb @@ -321,7 +321,7 @@ def trigger_batch_async(*events) # Generate the expected response for an authentication endpoint. - # See http://pusher.com/docs/authenticating_users for details. + # See https://pusher.com/docs/channels/server_api/authorizing-users for details. # # @example Private channels # render :json => Pusher.authenticate('private-my_channel', params[:socket_id]) From fa06be46a7535e9e9d34180f1df1088c573ebcd8 Mon Sep 17 00:00:00 2001 From: Johann Wilfrid-Calixte Date: Tue, 4 Jul 2023 14:11:05 +0200 Subject: [PATCH 2/9] feat: Add test for authenticate_user method --- spec/client_spec.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/spec/client_spec.rb b/spec/client_spec.rb index f374d1f..474f83e 100644 --- a/spec/client_spec.rb +++ b/spec/client_spec.rb @@ -310,6 +310,24 @@ end + describe '#authenticate_user' do + before :each do + @user_data = {:id => '123', :foo => { :name => 'Bar' }} + end + + it 'should return a hash with signature including custom data and data as json string' do + allow(MultiJson).to receive(:encode).with(@user_data).and_return 'a json string' + + response = @client.authenticate_user('1.1', @user_data) + + expect(response).to eq({ + :auth => "12345678900000001:#{hmac(@client.secret, "1.1::user::a json string")}", + :user_data => 'a json string' + }) + end + + end + describe '#trigger' do before :each do @api_path = %r{/apps/20/events} From 63305218315026cf67ad15c9bb21a58d33f970e4 Mon Sep 17 00:00:00 2001 From: Johann Wilfrid-Calixte Date: Tue, 4 Jul 2023 14:11:21 +0200 Subject: [PATCH 3/9] feat: Add authenticate_user method --- lib/pusher/client.rb | 72 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/lib/pusher/client.rb b/lib/pusher/client.rb index df3c0cd..4d89096 100644 --- a/lib/pusher/client.rb +++ b/lib/pusher/client.rb @@ -355,6 +355,31 @@ def authenticate(channel_name, socket_id, custom_data = nil) r end + # Generate the expected response for a user authentication endpoint. + # See https://pusher.com/docs/authenticating_users for details. + # + # @example + # user_data = { id: current_user.id.to_s, company_id: current_user.company_id } + # render :json => Pusher.authenticate_user(params[:socket_id], user_data) + # + # @param socket_id [String] + # @param user_data [Hash] user's properties (id is required and must be a string) + # + # @return [Hash] + # + # @raise [Pusher::Error] if socket_id or user_data is invalid + # + # @private Custom data is sent to server as JSON-encoded string + # + def authenticate_user(socket_id, user_data) + validate_user_data(user_data) + + custom_data = MultiJson.encode(user_data) + auth = authentication_string(socket_id, custom_data) + + { auth:, user_data: custom_data } + end + # @private Construct a net/http http client def sync_http_client require 'httpclient' @@ -469,5 +494,52 @@ def require_rbnacl $stderr.puts "You don't have rbnacl installed in your application. Please add it to your Gemfile and run bundle install" raise e end + + # Compute authentication string required as part of the user authentication + # endpoint response. Generally the authenticate method should be used in + # preference to this one. + # + # @param socket_id [String] Each Pusher socket connection receives a + # unique socket_id. This is sent from pusher.js to your server when + # channel authentication is required. + # @param custom_string [String] Allows signing additional data + # @return [String] + # + # @raise [Pusher::Error] if socket_id or custom_string invalid + # + def authentication_string(socket_id, custom_string = nil) + validate_socket_id(socket_id) + + raise Pusher::Error, 'Custom argument must be a string' unless custom_string.nil? || custom_string.is_a?(String) + + string_to_sign = [ + socket_id, + 'user', + custom_string + ].compact.map(&:to_s).join('::') + + Pusher.logger.debug "Signing #{string_to_sign}" + + digest = OpenSSL::Digest.new('SHA256') + signature = OpenSSL::HMAC.hexdigest(digest, authentication_token.secret, string_to_sign) + + "#{authentication_token.key}:#{signature}" + end + + def validate_socket_id(socket_id) + unless socket_id && /\A\d+\.\d+\z/.match(socket_id) + raise Pusher::Error, "Invalid socket ID #{socket_id.inspect}" + end + end + + def validate_user_data(user_data) + return if user_data_valid?(user_data) + + raise Pusher::Error, "Invalid user data #{user_data.inspect}" + end + + def user_data_valid?(data) + data.is_a?(Hash) && data.key?(:id) && !data[:id].empty? && data[:id].is_a?(String) + end end end From cf86de7f003181a9b2ba453aff29c58ff597fded Mon Sep 17 00:00:00 2001 From: Johann Wilfrid-Calixte Date: Tue, 4 Jul 2023 14:15:09 +0200 Subject: [PATCH 4/9] chore: extract validate_socket_id method in shared utils --- lib/pusher.rb | 1 + lib/pusher/channel.rb | 6 +----- lib/pusher/client.rb | 8 ++------ lib/pusher/utils.rb | 9 +++++++++ 4 files changed, 13 insertions(+), 11 deletions(-) create mode 100644 lib/pusher/utils.rb diff --git a/lib/pusher.rb b/lib/pusher.rb index 0c269ff..4537d93 100644 --- a/lib/pusher.rb +++ b/lib/pusher.rb @@ -2,6 +2,7 @@ require 'uri' require 'forwardable' +require 'pusher/utils' require 'pusher/client' # Used for configuring API credentials and creating Channel objects diff --git a/lib/pusher/channel.rb b/lib/pusher/channel.rb index e3f8c39..3795685 100644 --- a/lib/pusher/channel.rb +++ b/lib/pusher/channel.rb @@ -185,10 +185,6 @@ def shared_secret(encryption_master_key) private - def validate_socket_id(socket_id) - unless socket_id && /\A\d+\.\d+\z/.match(socket_id) - raise Pusher::Error, "Invalid socket ID #{socket_id.inspect}" - end - end + include Pusher::Utils end end diff --git a/lib/pusher/client.rb b/lib/pusher/client.rb index 4d89096..fddacce 100644 --- a/lib/pusher/client.rb +++ b/lib/pusher/client.rb @@ -424,6 +424,8 @@ def em_http_client(uri) private + include Pusher::Utils + def trigger_params(channels, event_name, data, params) channels = Array(channels).map(&:to_s) raise Pusher::Error, "Too many channels (#{channels.length}), max 100" if channels.length > 100 @@ -526,12 +528,6 @@ def authentication_string(socket_id, custom_string = nil) "#{authentication_token.key}:#{signature}" end - def validate_socket_id(socket_id) - unless socket_id && /\A\d+\.\d+\z/.match(socket_id) - raise Pusher::Error, "Invalid socket ID #{socket_id.inspect}" - end - end - def validate_user_data(user_data) return if user_data_valid?(user_data) diff --git a/lib/pusher/utils.rb b/lib/pusher/utils.rb new file mode 100644 index 0000000..3a442bd --- /dev/null +++ b/lib/pusher/utils.rb @@ -0,0 +1,9 @@ +module Pusher + module Utils + def validate_socket_id(socket_id) + unless socket_id && /\A\d+\.\d+\z/.match(socket_id) + raise Pusher::Error, "Invalid socket ID #{socket_id.inspect}" + end + end + end +end From 820584d8aaeca465d228fcecfa8ccb6802e1d2ba Mon Sep 17 00:00:00 2001 From: Johann Wilfrid-Calixte Date: Tue, 4 Jul 2023 14:22:40 +0200 Subject: [PATCH 5/9] chore: extract core logic from authentication_string method in shared utils --- lib/pusher/channel.rb | 15 ++------------- lib/pusher/client.rb | 17 ++--------------- lib/pusher/utils.rb | 25 +++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/lib/pusher/channel.rb b/lib/pusher/channel.rb index 3795685..9c6de18 100644 --- a/lib/pusher/channel.rb +++ b/lib/pusher/channel.rb @@ -126,20 +126,9 @@ def users(params = {}) # @raise [Pusher::Error] if socket_id or custom_string invalid # def authentication_string(socket_id, custom_string = nil) - validate_socket_id(socket_id) + string_to_sign = [socket_id, name, custom_string].compact.map(&:to_s).join(':') - unless custom_string.nil? || custom_string.kind_of?(String) - raise Error, 'Custom argument must be a string' - end - - string_to_sign = [socket_id, name, custom_string]. - compact.map(&:to_s).join(':') - Pusher.logger.debug "Signing #{string_to_sign}" - token = @client.authentication_token - digest = OpenSSL::Digest::SHA256.new - signature = OpenSSL::HMAC.hexdigest(digest, token.secret, string_to_sign) - - return "#{token.key}:#{signature}" + _authentication_string(socket_id, string_to_sign, @client.authentication_token, string_to_sign) end # Generate the expected response for an authentication endpoint. diff --git a/lib/pusher/client.rb b/lib/pusher/client.rb index fddacce..0de5841 100644 --- a/lib/pusher/client.rb +++ b/lib/pusher/client.rb @@ -510,22 +510,9 @@ def require_rbnacl # @raise [Pusher::Error] if socket_id or custom_string invalid # def authentication_string(socket_id, custom_string = nil) - validate_socket_id(socket_id) + string_to_sign = [socket_id, 'user', custom_string].compact.map(&:to_s).join('::') - raise Pusher::Error, 'Custom argument must be a string' unless custom_string.nil? || custom_string.is_a?(String) - - string_to_sign = [ - socket_id, - 'user', - custom_string - ].compact.map(&:to_s).join('::') - - Pusher.logger.debug "Signing #{string_to_sign}" - - digest = OpenSSL::Digest.new('SHA256') - signature = OpenSSL::HMAC.hexdigest(digest, authentication_token.secret, string_to_sign) - - "#{authentication_token.key}:#{signature}" + _authentication_string(socket_id, string_to_sign, authentication_token, string_to_sign) end def validate_user_data(user_data) diff --git a/lib/pusher/utils.rb b/lib/pusher/utils.rb index 3a442bd..7fffffd 100644 --- a/lib/pusher/utils.rb +++ b/lib/pusher/utils.rb @@ -5,5 +5,30 @@ def validate_socket_id(socket_id) raise Pusher::Error, "Invalid socket ID #{socket_id.inspect}" end end + + # Compute authentication string required as part of the user authentication + # and subscription authorization endpoints responses. + # Generally the authenticate method should be used in preference to this one. + # + # @param socket_id [String] Each Pusher socket connection receives a + # unique socket_id. This is sent from pusher.js to your server when + # channel authentication is required. + # @param custom_string [String] Allows signing additional data + # @return [String] + # + # @raise [Pusher::Error] if socket_id or custom_string invalid + # + def _authentication_string(socket_id, string_to_sign, token, custom_string = nil) + validate_socket_id(socket_id) + + raise Pusher::Error, 'Custom argument must be a string' unless custom_string.nil? || custom_string.is_a?(String) + + Pusher.logger.debug "Signing #{string_to_sign}" + + digest = OpenSSL::Digest.new('SHA256') + signature = OpenSSL::HMAC.hexdigest(digest, token.secret, string_to_sign) + + "#{token.key}:#{signature}" + end end end From 9747f2966e447f4278c746d0c0528139ddd82d0a Mon Sep 17 00:00:00 2001 From: 40handz Date: Tue, 17 Mar 2026 17:23:47 -0400 Subject: [PATCH 6/9] Bump to version 2.0.4 - Add authenticate_user method for user authentication flow - Fix authentication_string validation in channel to correctly check custom_string argument type --- CHANGELOG.md | 18 +++++++++++------- lib/pusher/channel.rb | 2 +- lib/pusher/version.rb | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f1b2d1..02bde10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,24 +1,28 @@ # Changelog +## 2.0.4 + +* [ADDED] Add `authenticate_user` method for user authentication flow + ## 2.0.3 * [FIXED] Corrected the channels limit when publishing events. Upped from 10 to 100. ## 2.0.2 -* [CHANGED] made encryption_master_key_base64 globally configurable +* [CHANGED] made encryption_master_key_base64 globally configurable ## 2.0.1 -* [CHANGED] Only include lib and essential docs in gem. +* [CHANGED] Only include lib and essential docs in gem. ## 2.0.0 -* [CHANGED] Use TLS by default. -* [REMOVED] Support for Ruby 2.4 and 2.5. -* [FIXED] Handle empty or nil configuration. -* [REMOVED] Legacy Push Notification integration. -* [ADDED] Stalebot and Github actions. +* [CHANGED] Use TLS by default. +* [REMOVED] Support for Ruby 2.4 and 2.5. +* [FIXED] Handle empty or nil configuration. +* [REMOVED] Legacy Push Notification integration. +* [ADDED] Stalebot and Github actions. ## 1.4.3 diff --git a/lib/pusher/channel.rb b/lib/pusher/channel.rb index 9c6de18..5de06a7 100644 --- a/lib/pusher/channel.rb +++ b/lib/pusher/channel.rb @@ -128,7 +128,7 @@ def users(params = {}) def authentication_string(socket_id, custom_string = nil) string_to_sign = [socket_id, name, custom_string].compact.map(&:to_s).join(':') - _authentication_string(socket_id, string_to_sign, @client.authentication_token, string_to_sign) + _authentication_string(socket_id, string_to_sign, @client.authentication_token, custom_string) end # Generate the expected response for an authentication endpoint. diff --git a/lib/pusher/version.rb b/lib/pusher/version.rb index 9d1db9e..19f41b7 100644 --- a/lib/pusher/version.rb +++ b/lib/pusher/version.rb @@ -1,3 +1,3 @@ module Pusher - VERSION = '2.0.3' + VERSION = '2.0.4' end From 9a48ea811e073f2e3d37d8b558cf07da6a6570be Mon Sep 17 00:00:00 2001 From: 40handz Date: Wed, 18 Mar 2026 15:05:46 -0400 Subject: [PATCH 7/9] ci: update test workflow to ubuntu-latest and Ruby 3.2-3.4 ubuntu-20.04 runners were retired from GitHub Actions, causing jobs to be permanently stuck in queued state. Ruby 2.6, 2.7, and 3.0 are also EOL. Update to ubuntu-latest and currently supported Ruby versions. --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c292de3..36b3b44 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,11 +7,11 @@ on: jobs: test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - ruby: ['2.6', '2.7', '3.0'] + ruby: ['3.2', '3.3', '3.4'] name: Ruby ${{ matrix.ruby }} Test From 2142c3124d1e0a3104588771fab52b1ee3113be6 Mon Sep 17 00:00:00 2001 From: 40handz Date: Thu, 19 Mar 2026 10:30:11 -0400 Subject: [PATCH 8/9] Update deprecated GitHub Actions in workflows --- .github/workflows/gh-release.yml | 10 +++++----- .github/workflows/publish.yml | 4 ++-- .github/workflows/release.yml | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/gh-release.yml b/.github/workflows/gh-release.yml index 815fc86..92fd309 100644 --- a/.github/workflows/gh-release.yml +++ b/.github/workflows/gh-release.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup git run: | git config user.email "pusher-ci@pusher.com" @@ -24,12 +24,12 @@ jobs: export TAG=$(head -1 CHANGELOG.tmp | cut -d' ' -f2) echo "TAG=$TAG" >> $GITHUB_ENV - name: Create Release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: softprops/action-gh-release@v2.6.1 with: tag_name: ${{ env.TAG }} - release_name: ${{ env.TAG }} + name: ${{ env.TAG }} body_path: CHANGELOG.tmp draft: false prerelease: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e8b9a99..a2386c9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,9 +9,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Publish gem - uses: dawidd6/action-publish-gem@v1 + uses: dawidd6/action-publish-gem@v1.2.0 with: api_key: ${{secrets.RUBYGEMS_API_KEY}} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c0a83b1..60692f5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,7 +43,7 @@ jobs: export NEW_VERSION=$(semver bump ${{ env.RELEASE }} $CURRENT) echo "VERSION=$NEW_VERSION" >> $GITHUB_ENV - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup git run: | git config user.email "pusher-ci@pusher.com" From fb0cc2b6f92c91453fd302bd3745a9d2e4f903b2 Mon Sep 17 00:00:00 2001 From: 40handz Date: Thu, 19 Mar 2026 10:38:30 -0400 Subject: [PATCH 9/9] Update actions/checkout to v4 in test.yml --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 36b3b44..678157f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup Ruby uses: ruby/setup-ruby@v1