authsignal-ruby 5.2.1 → 5.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eb8afa36ec62970a06618e860b3f343d4e7d7c81ce70e45d905e69c5cccf9c83
4
- data.tar.gz: a55a775d64b0569d044f1f4f4362717a160fbb214f63f56af2e11c5d391dc1de
3
+ metadata.gz: cc7b157f7e5e965983377b879308969e0e64cdaa9e9c898befcb960882768d87
4
+ data.tar.gz: 3a52e2684801ab9e9825f5a053c9bbdeb58a663e714613e7a2994b064354793a
5
5
  SHA512:
6
- metadata.gz: 1d0bf703634455560509482b03ff746828ac10dd824ebe4d83bfc2795e5c6f8db7f45c1a7655233bfb762a0034b8fa98285275510170b4acd03194b4e9630ffd
7
- data.tar.gz: 6d724b25f111840ff0517ccf6e7c6c5a203c4de48e00a459e296b3ab6ea15a86ba6c31fb3db9961cc5ca0616e82fb7cec5b8c8e52027459a231561409ad249cc
6
+ metadata.gz: 814653fbdea511018febd90b03d4be4f75de814e12cf71c96ca560eaaad078ffbc496ae48b0e43a178ceab49f1dba7ea280a2fcd2bf4202393c0fa52e7970b5e
7
+ data.tar.gz: f2f939e03b2a02a572f69fe65c6907904ef0947d27ef5bb41274a0fe40e73c346656883af5ffa765504264041f5fb3f3584bde954da69fc5878d79869c19fa26
data/.editorconfig ADDED
@@ -0,0 +1,19 @@
1
+ # EditorConfig helps maintain consistent coding styles
2
+ # https://editorconfig.org
3
+
4
+ root = true
5
+
6
+ [*]
7
+ indent_style = space
8
+ indent_size = 2
9
+ end_of_line = lf
10
+ charset = utf-8
11
+ trim_trailing_whitespace = true
12
+ insert_final_newline = true
13
+
14
+ [*.md]
15
+ trim_trailing_whitespace = false
16
+
17
+ [*.{yml,yaml}]
18
+ indent_size = 2
19
+
data/.rubocop.yml ADDED
@@ -0,0 +1,47 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ TargetRubyVersion: 2.6
4
+ SuggestExtensions: false
5
+ Exclude:
6
+ - 'vendor/**/*'
7
+
8
+ # Enforce 2-space indentation
9
+ Layout/IndentationWidth:
10
+ Width: 2
11
+
12
+ Layout/LineLength:
13
+ Max: 150
14
+
15
+ # Be lenient in specs and gemspec
16
+ Metrics/BlockLength:
17
+ Exclude:
18
+ - 'spec/**/*'
19
+ - '*.gemspec'
20
+
21
+ # SDK methods can have more parameters
22
+ Metrics/ParameterLists:
23
+ Max: 15
24
+
25
+ # SDK classes can be longer
26
+ Metrics/ClassLength:
27
+ Max: 150
28
+
29
+ Metrics/MethodLength:
30
+ Max: 25
31
+
32
+ Metrics/AbcSize:
33
+ Max: 30
34
+
35
+ # Skip documentation requirement for SDK
36
+ Style/Documentation:
37
+ Enabled: false
38
+
39
+ # Gemspec specific
40
+ Gemspec/DevelopmentDependencies:
41
+ Enabled: false
42
+
43
+ Gemspec/RequireMFA:
44
+ Enabled: false
45
+
46
+ Gemspec/OrderedDependencies:
47
+ Enabled: false
data/Gemfile CHANGED
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source "https://rubygems.org"
3
+ source 'https://rubygems.org'
4
4
 
5
5
  # Specify your gem's dependencies in authsignal-ruby.gemspec
6
6
  gemspec
7
7
 
8
- group :test do
8
+ group :development, :test do
9
9
  gem 'dotenv'
10
+ gem 'rubocop', require: false
10
11
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- authsignal-ruby (5.2.1)
4
+ authsignal-ruby (5.2.2)
5
5
  base64
6
6
  faraday (>= 2.0.1)
7
7
  faraday-retry (~> 2.2)
@@ -9,52 +9,69 @@ PATH
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- addressable (2.8.7)
13
- public_suffix (>= 2.0.2, < 7.0)
14
- base64 (0.2.0)
15
- bigdecimal (3.1.8)
16
- crack (1.0.0)
12
+ addressable (2.8.8)
13
+ public_suffix (>= 2.0.2, < 8.0)
14
+ ast (2.4.3)
15
+ base64 (0.3.0)
16
+ bigdecimal (4.0.1)
17
+ crack (1.0.1)
17
18
  bigdecimal
18
19
  rexml
19
- diff-lcs (1.5.1)
20
- dotenv (3.1.7)
21
- faraday (2.12.0)
22
- faraday-net_http (>= 2.0, < 3.4)
23
- json
24
- logger
25
- faraday-net_http (3.3.0)
26
- net-http
27
- faraday-retry (2.2.1)
20
+ diff-lcs (1.6.2)
21
+ dotenv (2.8.1)
22
+ faraday (2.8.1)
23
+ base64
24
+ faraday-net_http (>= 2.0, < 3.1)
25
+ ruby2_keywords (>= 0.0.4)
26
+ faraday-net_http (3.0.2)
27
+ faraday-retry (2.4.0)
28
28
  faraday (~> 2.0)
29
- hashdiff (1.1.1)
30
- json (2.7.2)
31
- logger (1.6.1)
32
- net-http (0.4.1)
33
- uri
34
- public_suffix (6.0.1)
35
- rake (13.2.1)
36
- rexml (3.3.8)
37
- rspec (3.13.0)
29
+ hashdiff (1.2.1)
30
+ json (2.7.6)
31
+ parallel (1.24.0)
32
+ parser (3.3.10.0)
33
+ ast (~> 2.4.1)
34
+ racc
35
+ public_suffix (5.1.1)
36
+ racc (1.8.1)
37
+ rainbow (3.1.1)
38
+ rake (13.3.1)
39
+ regexp_parser (2.11.3)
40
+ rexml (3.4.4)
41
+ rspec (3.13.2)
38
42
  rspec-core (~> 3.13.0)
39
43
  rspec-expectations (~> 3.13.0)
40
44
  rspec-mocks (~> 3.13.0)
41
- rspec-core (3.13.2)
45
+ rspec-core (3.13.6)
42
46
  rspec-support (~> 3.13.0)
43
- rspec-expectations (3.13.3)
47
+ rspec-expectations (3.13.5)
44
48
  diff-lcs (>= 1.2.0, < 2.0)
45
49
  rspec-support (~> 3.13.0)
46
- rspec-mocks (3.13.2)
50
+ rspec-mocks (3.13.7)
47
51
  diff-lcs (>= 1.2.0, < 2.0)
48
52
  rspec-support (~> 3.13.0)
49
- rspec-support (3.13.1)
50
- uri (0.13.1)
51
- webmock (3.24.0)
53
+ rspec-support (3.13.6)
54
+ rubocop (1.50.2)
55
+ json (~> 2.3)
56
+ parallel (~> 1.10)
57
+ parser (>= 3.2.0.0)
58
+ rainbow (>= 2.2.2, < 4.0)
59
+ regexp_parser (>= 1.8, < 3.0)
60
+ rexml (>= 3.2.5, < 4.0)
61
+ rubocop-ast (>= 1.28.0, < 2.0)
62
+ ruby-progressbar (~> 1.7)
63
+ unicode-display_width (>= 2.4.0, < 3.0)
64
+ rubocop-ast (1.30.0)
65
+ parser (>= 3.2.1.0)
66
+ ruby-progressbar (1.13.0)
67
+ ruby2_keywords (0.0.5)
68
+ unicode-display_width (2.6.0)
69
+ webmock (3.26.1)
52
70
  addressable (>= 2.8.0)
53
71
  crack (>= 0.3.2)
54
72
  hashdiff (>= 0.4.0, < 2.0.0)
55
73
 
56
74
  PLATFORMS
57
- arm64-darwin-24
58
75
  ruby
59
76
 
60
77
  DEPENDENCIES
@@ -62,7 +79,8 @@ DEPENDENCIES
62
79
  dotenv
63
80
  rake (~> 13.0)
64
81
  rspec (~> 3.2)
82
+ rubocop
65
83
  webmock (~> 3.14)
66
84
 
67
85
  BUNDLED WITH
68
- 2.5.14
86
+ 1.17.2
data/Rakefile CHANGED
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
5
 
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
- task default: :spec
8
+ task default: :spec
data/bin/console CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "bundler/setup"
5
- require "authsignal"
4
+ require 'bundler/setup'
5
+ require 'authsignal'
6
6
 
7
7
  # You can add fixtures and/or initialization code here to make experimenting
8
8
  # with your gem easier. You can also use a different console, if you like.
@@ -11,5 +11,5 @@ require "authsignal"
11
11
  # require "pry"
12
12
  # Pry.start
13
13
 
14
- require "irb"
14
+ require 'irb'
15
15
  IRB.start(__FILE__)
@@ -25,7 +25,7 @@ module Authsignal
25
25
  end
26
26
 
27
27
  def format_description(error_code, error_description)
28
- error_description && error_description.length > 0 ? error_description : error_code
28
+ error_description&.length&.positive? ? error_description : error_code
29
29
  end
30
30
  end
31
31
  end
@@ -1,117 +1,117 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'erb'
2
4
 
3
5
  module Authsignal
4
- class Client
5
- USER_AGENT = "authsignal-ruby"
6
- NO_API_KEY_MESSAGE = "No Authsignal API Secret Key Set"
7
-
8
- RETRY_OPTIONS = {
9
- max: 3,
10
- interval: 0.1,
11
- interval_randomness: 0.5,
12
- backoff_factor: 2,
13
- }.freeze
14
- private_constant :RETRY_OPTIONS
15
-
16
- def initialize(retry_options: RETRY_OPTIONS)
17
- @api_key = require_api_key
18
-
19
- @client = Faraday.new do |builder|
20
- builder.url_prefix = Authsignal.configuration.api_url
21
- builder.adapter :net_http
22
- builder.request :authorization, :basic, @api_key, nil
23
-
24
- builder.headers['Accept'] = 'application/json'
25
- builder.headers['Content-Type'] = 'application/json'
26
- builder.headers['User-Agent'] = USER_AGENT
27
- builder.headers['X-Authsignal-Version'] = Authsignal::VERSION
28
-
29
- builder.request :json
30
- builder.response :json, parser_options: { symbolize_names: true }
31
-
32
- builder.use Middleware::JsonRequest
33
- builder.use Middleware::JsonResponse
34
-
35
- builder.request :retry, retry_options if Authsignal.configuration.retry
36
- builder.response :logger, ::Logger.new(STDOUT), bodies: true if Authsignal.configuration.debug
37
- end
38
- end
39
-
40
- def get_user(user_id:)
41
- path = "users/#{url_encode(user_id)}"
42
- make_request(:get, path)
43
- end
44
-
45
- def update_user(user_id:, attributes:)
46
- make_request(:post, "users/#{url_encode(user_id)}", body: attributes)
47
- end
48
-
49
- def delete_user(user_id:)
50
- make_request(:delete, "users/#{url_encode(user_id)}")
51
- end
52
-
53
- def get_authenticators(user_id:)
54
- make_request(:get, "users/#{url_encode(user_id)}/authenticators")
55
- end
56
-
57
- def enroll_verified_authenticator(user_id:, attributes:)
58
- make_request(:post, "users/#{url_encode(user_id)}/authenticators", body: attributes)
59
- end
60
-
61
- def delete_authenticator(user_id:, user_authenticator_id:)
62
- make_request(:delete, "users/#{url_encode(user_id)}/authenticators/#{url_encode(user_authenticator_id)}")
63
- end
64
-
65
- def track(user_id:, action:, attributes:)
66
- path = "users/#{user_id}/actions/#{action}"
67
-
68
- make_request(:post, path, body: attributes)
69
- end
70
-
71
- def validate_challenge(token:, user_id: nil, action: nil)
72
- path = "validate"
73
- body = { user_id: user_id, token: token, action: action }
74
-
75
- make_request(:post, path, body: body)
76
- end
77
-
78
- def get_action(user_id:, action:, idempotency_key:)
79
- make_request(:get, "users/#{url_encode(user_id)}/actions/#{action}/#{url_encode(idempotency_key)}")
80
- end
81
-
82
- def update_action(user_id:, action:, idempotency_key:, attributes:)
83
- make_request(:patch, "users/#{url_encode(user_id)}/actions/#{action}/#{url_encode(idempotency_key)}", body: attributes)
84
- end
85
-
86
- ##
87
- # TODO: delete identify?
88
- def identify(user_id, user_payload)
89
- make_request(:post , "users/#{url_encode(user_id)}", body: user_payload)
90
- end
91
-
92
- private
93
-
94
- def url_encode(s)
95
- ERB::Util.url_encode(s)
96
- end
97
-
98
- def version
99
- Authsignal.configuration.version
100
- end
101
-
102
- def print_api_key_warning
103
- $stderr.puts(NO_API_KEY_MESSAGE)
104
- end
105
-
106
- def require_api_key
107
- Authsignal.configuration.api_secret_key || print_api_key_warning
108
- end
109
-
110
- def make_request(method, path, body: nil, headers: nil)
111
- if body.is_a?(Hash)
112
- body = body.reject { |_, v| v.nil? }
113
- end
114
- @client.public_send(method, path, body, headers)
115
- end
6
+ class Client
7
+ USER_AGENT = 'authsignal-ruby'
8
+ NO_API_KEY_MESSAGE = 'No Authsignal API Secret Key Set'
9
+
10
+ RETRY_OPTIONS = {
11
+ max: 3,
12
+ interval: 0.1,
13
+ interval_randomness: 0.5,
14
+ backoff_factor: 2
15
+ }.freeze
16
+ private_constant :RETRY_OPTIONS
17
+
18
+ def initialize(retry_options: RETRY_OPTIONS)
19
+ @api_key = require_api_key
20
+
21
+ @client = Faraday.new do |builder|
22
+ builder.url_prefix = Authsignal.configuration.api_url
23
+ builder.adapter :net_http
24
+ builder.request :authorization, :basic, @api_key, nil
25
+
26
+ builder.headers['Accept'] = 'application/json'
27
+ builder.headers['Content-Type'] = 'application/json'
28
+ builder.headers['User-Agent'] = USER_AGENT
29
+ builder.headers['X-Authsignal-Version'] = Authsignal::VERSION
30
+
31
+ builder.request :json
32
+ builder.response :json, parser_options: { symbolize_names: true }
33
+
34
+ builder.use Middleware::JsonRequest
35
+ builder.use Middleware::JsonResponse
36
+
37
+ builder.request :retry, retry_options if Authsignal.configuration.retry
38
+ builder.response :logger, ::Logger.new($stdout), bodies: true if Authsignal.configuration.debug
39
+ end
40
+ end
41
+
42
+ def get_user(user_id:)
43
+ path = "users/#{url_encode(user_id)}"
44
+ make_request(:get, path)
45
+ end
46
+
47
+ def update_user(user_id:, attributes:)
48
+ make_request(:post, "users/#{url_encode(user_id)}", body: attributes)
49
+ end
50
+
51
+ def delete_user(user_id:)
52
+ make_request(:delete, "users/#{url_encode(user_id)}")
53
+ end
54
+
55
+ def get_authenticators(user_id:)
56
+ make_request(:get, "users/#{url_encode(user_id)}/authenticators")
57
+ end
58
+
59
+ def enroll_verified_authenticator(user_id:, attributes:)
60
+ make_request(:post, "users/#{url_encode(user_id)}/authenticators", body: attributes)
61
+ end
62
+
63
+ def delete_authenticator(user_id:, user_authenticator_id:)
64
+ make_request(:delete, "users/#{url_encode(user_id)}/authenticators/#{url_encode(user_authenticator_id)}")
65
+ end
66
+
67
+ def track(user_id:, action:, attributes:)
68
+ path = "users/#{user_id}/actions/#{action}"
69
+
70
+ make_request(:post, path, body: attributes)
71
+ end
72
+
73
+ def validate_challenge(token:, user_id: nil, action: nil)
74
+ path = 'validate'
75
+ body = { user_id: user_id, token: token, action: action }
76
+
77
+ make_request(:post, path, body: body)
78
+ end
79
+
80
+ def get_action(user_id:, action:, idempotency_key:)
81
+ make_request(:get, "users/#{url_encode(user_id)}/actions/#{action}/#{url_encode(idempotency_key)}")
82
+ end
83
+
84
+ def update_action(user_id:, action:, idempotency_key:, attributes:)
85
+ make_request(:patch, "users/#{url_encode(user_id)}/actions/#{action}/#{url_encode(idempotency_key)}", body: attributes)
86
+ end
87
+
88
+ ##
89
+ # TODO: delete identify?
90
+ def identify(user_id, user_payload)
91
+ make_request(:post, "users/#{url_encode(user_id)}", body: user_payload)
92
+ end
93
+
94
+ private
95
+
96
+ def url_encode(str)
97
+ ERB::Util.url_encode(str)
98
+ end
99
+
100
+ def version
101
+ Authsignal.configuration.version
102
+ end
103
+
104
+ def print_api_key_warning
105
+ warn(NO_API_KEY_MESSAGE)
106
+ end
107
+
108
+ def require_api_key
109
+ Authsignal.configuration.api_secret_key || print_api_key_warning
110
+ end
111
+
112
+ def make_request(method, path, body: nil, headers: nil)
113
+ body = body.compact if body.is_a?(Hash)
114
+ @client.public_send(method, path, body, headers)
116
115
  end
116
+ end
117
117
  end
@@ -1,52 +1,55 @@
1
- require "ostruct"
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Authsignal
4
4
  class Configuration
5
- def self.config_option(name)
6
- define_method(name) do
7
- read_value(name)
8
- end
9
-
10
- define_method("#{name}=") do |value|
11
- set_value(name, value)
12
- end
13
- end
14
-
15
- config_option :api_secret_key
16
- config_option :api_url
17
- config_option :debug
18
- config_option :retry
19
-
20
- def initialize
21
- @config_values = {}
22
-
23
- # set default attribute values
24
- @defaults = OpenStruct.new({
25
- api_url: 'https://signal.authsignal.com/v1/',
26
- retry: false,
27
- debug: false
28
- })
29
- end
30
-
31
- def [](key)
32
- read_value(key)
33
- end
34
-
35
- def []=(key, value)
36
- set_value(key, value)
37
- end
38
-
39
- private
40
- def read_value(name)
41
- if @config_values.has_key?(name)
42
- @config_values[name]
43
- else
44
- @defaults.send(name)
45
- end
46
- end
47
-
48
- def set_value(name, value)
49
- @config_values[name] = value
50
- end
51
- end
5
+ attr_reader :defaults
6
+
7
+ def self.config_option(name)
8
+ define_method(name) do
9
+ read_value(name)
10
+ end
11
+
12
+ define_method("#{name}=") do |value|
13
+ set_value(name, value)
14
+ end
15
+ end
16
+
17
+ config_option :api_secret_key
18
+ config_option :api_url
19
+ config_option :debug
20
+ config_option :retry
21
+
22
+ def initialize
23
+ @config_values = {}
24
+
25
+ # set default attribute values
26
+ @defaults = {
27
+ api_url: 'https://signal.authsignal.com/v1/',
28
+ retry: false,
29
+ debug: false
30
+ }
31
+ end
32
+
33
+ def [](key)
34
+ read_value(key)
35
+ end
36
+
37
+ def []=(key, value)
38
+ set_value(key, value)
39
+ end
40
+
41
+ private
42
+
43
+ def read_value(name)
44
+ if @config_values.key?(name)
45
+ @config_values[name]
46
+ else
47
+ @defaults[name]
48
+ end
49
+ end
50
+
51
+ def set_value(name, value)
52
+ @config_values[name] = value
53
+ end
54
+ end
52
55
  end
@@ -1,7 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Authsignal
2
4
  class InvalidSignatureError < StandardError
3
- def initialize(message)
4
- super(message)
5
- end
6
5
  end
7
6
  end
@@ -7,9 +7,7 @@ module Authsignal
7
7
  return if env.body.nil?
8
8
 
9
9
  parsed_body = JSON.parse(env.body)
10
- if parsed_body.is_a?(Hash)
11
- env.body = camelcase_keys(parsed_body).to_json
12
- end
10
+ env.body = camelcase_keys(parsed_body).to_json if parsed_body.is_a?(Hash)
13
11
  rescue JSON::ParserError
14
12
  # noop
15
13
  end
@@ -9,7 +9,7 @@ module Authsignal
9
9
  # Otherwise, we can safe guard with: env.response_headers['content-type'] =~ /application\/json/
10
10
  parsed_body = JSON.parse(env.body)
11
11
  if parsed_body.is_a?(Hash)
12
- parsed_body.delete("actionCode") # Remove deprecated actionCode from response
12
+ parsed_body.delete('actionCode') # Remove deprecated actionCode from response
13
13
  env.body = transform_to_snake_case(parsed_body)
14
14
  end
15
15
  rescue JSON::ParserError
@@ -20,9 +20,10 @@ module Authsignal
20
20
 
21
21
  def underscore(camelcased)
22
22
  return camelcased.to_s unless /[A-Z-]|::/.match?(camelcased)
23
- word = camelcased.to_s.gsub("::", "/")
24
- word.gsub!(/([A-Z])(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { ($1 || $2) << "_" }
25
- word.tr!("-", "_")
23
+
24
+ word = camelcased.to_s.gsub('::', '/')
25
+ word.gsub!(/([A-Z])(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { (::Regexp.last_match(1) || ::Regexp.last_match(2)) << '_' }
26
+ word.tr!('-', '_')
26
27
  word.downcase!
27
28
  word
28
29
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Authsignal
4
- VERSION = "5.2.1"
4
+ VERSION = '5.2.2'
5
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'openssl'
2
4
  require 'json'
3
5
  require 'base64'
@@ -6,7 +8,7 @@ module Authsignal
6
8
  DEFAULT_TOLERANCE = 5
7
9
 
8
10
  class Webhook
9
- VERSION = "v2"
11
+ VERSION = 'v2'
10
12
 
11
13
  attr_reader :api_secret_key
12
14
 
@@ -19,8 +21,8 @@ module Authsignal
19
21
 
20
22
  seconds_since_epoch = Time.now.to_i
21
23
 
22
- if tolerance > 0 && parsed_signature[:timestamp] < seconds_since_epoch - (tolerance * 60)
23
- raise InvalidSignatureError, "Timestamp is outside the tolerance zone."
24
+ if tolerance.positive? && parsed_signature[:timestamp] < seconds_since_epoch - (tolerance * 60)
25
+ raise InvalidSignatureError, 'Timestamp is outside the tolerance zone.'
24
26
  end
25
27
 
26
28
  hmac_content = "#{parsed_signature[:timestamp]}.#{payload}"
@@ -41,34 +43,29 @@ module Authsignal
41
43
  end
42
44
  end
43
45
 
44
- unless match
45
- raise InvalidSignatureError, "Signature mismatch."
46
- end
46
+ raise InvalidSignatureError, 'Signature mismatch.' unless match
47
47
 
48
48
  JSON.parse(payload, symbolize_names: true)
49
49
  end
50
50
 
51
51
  def parse_signature(value)
52
- result = {
53
- timestamp: -1,
54
- signatures: []
55
- }
52
+ handle_invalid_signature unless value
56
53
 
57
- return handle_invalid_signature unless value
54
+ result = extract_signature_parts(value)
55
+ handle_invalid_signature if result[:timestamp] == -1 || result[:signatures].empty?
58
56
 
59
- value.split(',').each do |item|
60
- kv = item.split('=')
61
- next unless kv.length == 2
57
+ result
58
+ end
62
59
 
63
- if kv[0] == 't'
64
- result[:timestamp] = kv[1].to_i
65
- elsif kv[0] == VERSION
66
- result[:signatures] << kv[1]
67
- end
68
- end
60
+ def extract_signature_parts(value)
61
+ result = { timestamp: -1, signatures: [] }
62
+
63
+ value.split(',').each do |item|
64
+ key, val = item.split('=')
65
+ next unless key && val
69
66
 
70
- if result[:timestamp] == -1 || result[:signatures].empty?
71
- handle_invalid_signature
67
+ result[:timestamp] = val.to_i if key == 't'
68
+ result[:signatures] << val if key == VERSION
72
69
  end
73
70
 
74
71
  result
@@ -77,7 +74,7 @@ module Authsignal
77
74
  private
78
75
 
79
76
  def handle_invalid_signature
80
- raise InvalidSignatureError, "Signature format is invalid."
77
+ raise InvalidSignatureError, 'Signature format is invalid.'
81
78
  end
82
79
  end
83
80
  end
data/lib/authsignal.rb CHANGED
@@ -1,133 +1,136 @@
1
- require "faraday"
2
- require "faraday/retry"
3
- require "authsignal/version"
4
- require "authsignal/client"
5
- require "authsignal/configuration"
6
- require "authsignal/api_error"
7
- require "authsignal/invalid_signature_error"
8
- require "authsignal/webhook"
9
- require "authsignal/middleware/json_response"
10
- require "authsignal/middleware/json_request"
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'faraday/retry'
5
+ require 'authsignal/version'
6
+ require 'authsignal/client'
7
+ require 'authsignal/configuration'
8
+ require 'authsignal/api_error'
9
+ require 'authsignal/invalid_signature_error'
10
+ require 'authsignal/webhook'
11
+ require 'authsignal/middleware/json_response'
12
+ require 'authsignal/middleware/json_request'
11
13
 
12
14
  module Authsignal
13
- NON_API_METHODS = [:setup, :configuration, :default_configuration, :webhook]
15
+ NON_API_METHODS = %i[setup configuration default_configuration webhook].freeze
14
16
 
15
- class << self
16
- attr_writer :configuration
17
+ class << self
18
+ attr_writer :configuration
17
19
 
18
- def setup
19
- yield(configuration)
20
- end
21
-
22
- def configuration
23
- @configuration ||= Authsignal::Configuration.new
24
- end
20
+ def setup
21
+ yield(configuration)
22
+ end
25
23
 
26
- def default_configuration
27
- configuration.defaults
28
- end
24
+ def configuration
25
+ @configuration ||= Authsignal::Configuration.new
26
+ end
29
27
 
30
- def webhook
31
- @webhook ||= Webhook.new(configuration.api_secret_key)
32
- end
28
+ def default_configuration
29
+ configuration.defaults
30
+ end
33
31
 
34
- def get_user(user_id:)
35
- response = Client.new.get_user(user_id: user_id)
32
+ def webhook
33
+ @webhook ||= Webhook.new(configuration.api_secret_key)
34
+ end
36
35
 
37
- handle_response(response)
38
- end
36
+ def get_user(user_id:)
37
+ response = Client.new.get_user(user_id: user_id)
39
38
 
40
- def update_user(user_id:, attributes:)
41
- response = Client.new.update_user(user_id: user_id, attributes: attributes)
39
+ handle_response(response)
40
+ end
42
41
 
43
- handle_response(response)
44
- end
42
+ def update_user(user_id:, attributes:)
43
+ response = Client.new.update_user(user_id: user_id, attributes: attributes)
45
44
 
46
- def delete_user(user_id:)
47
- response = Client.new.delete_user(user_id: user_id)
45
+ handle_response(response)
46
+ end
48
47
 
49
- handle_response(response)
50
- end
48
+ def delete_user(user_id:)
49
+ response = Client.new.delete_user(user_id: user_id)
51
50
 
52
- def get_authenticators(user_id:)
53
- response = Client.new.get_authenticators(user_id: user_id)
51
+ handle_response(response)
52
+ end
54
53
 
55
- handle_response(response)
56
- end
54
+ def get_authenticators(user_id:)
55
+ response = Client.new.get_authenticators(user_id: user_id)
57
56
 
58
- def enroll_verified_authenticator(user_id:, attributes:)
59
- response = Client.new.enroll_verified_authenticator(user_id: user_id, attributes: attributes)
57
+ handle_response(response)
58
+ end
60
59
 
61
- handle_response(response)
62
- end
60
+ def enroll_verified_authenticator(user_id:, attributes:)
61
+ response = Client.new.enroll_verified_authenticator(user_id: user_id, attributes: attributes)
63
62
 
64
- def delete_authenticator(user_id:, user_authenticator_id:)
65
- response = Client.new.delete_authenticator(user_id: user_id, user_authenticator_id: user_authenticator_id)
63
+ handle_response(response)
64
+ end
66
65
 
67
- handle_response(response)
68
- end
66
+ def delete_authenticator(user_id:, user_authenticator_id:)
67
+ response = Client.new.delete_authenticator(user_id: user_id, user_authenticator_id: user_authenticator_id)
69
68
 
70
- def track(user_id:, action:, attributes:)
71
- response = Client.new.track(user_id: user_id, action: action, attributes: attributes)
72
- handle_response(response)
73
- end
69
+ handle_response(response)
70
+ end
74
71
 
75
- def validate_challenge(token:, user_id: nil, action: nil)
76
- response = Client.new.validate_challenge(token: token,user_id: user_id, action: action)
77
-
78
- handle_response(response)
79
- end
72
+ def track(user_id:, action:, attributes:)
73
+ response = Client.new.track(user_id: user_id, action: action, attributes: attributes)
74
+ handle_response(response)
75
+ end
80
76
 
81
- def get_action(user_id:, action:, idempotency_key:)
82
- response = Client.new.get_action(user_id: user_id, action: action, idempotency_key: idempotency_key)
77
+ def validate_challenge(token:, user_id: nil, action: nil)
78
+ response = Client.new.validate_challenge(token: token, user_id: user_id, action: action)
83
79
 
84
- handle_response(response)
85
- end
80
+ handle_response(response)
81
+ end
86
82
 
87
- def update_action(user_id:, action:, idempotency_key:, attributes:)
88
- response = Client.new.update_action(user_id: user_id, action: action, idempotency_key: idempotency_key, attributes: attributes)
83
+ def get_action(user_id:, action:, idempotency_key:)
84
+ response = Client.new.get_action(user_id: user_id, action: action, idempotency_key: idempotency_key)
89
85
 
90
- handle_response(response)
91
- end
86
+ handle_response(response)
87
+ end
92
88
 
93
- private
89
+ def update_action(user_id:, action:, idempotency_key:, attributes:)
90
+ response = Client.new.update_action(user_id: user_id, action: action, idempotency_key: idempotency_key, attributes: attributes)
94
91
 
95
- def handle_response(response)
96
- if response.success?
97
- handle_success_response(response)
98
- else
99
- handle_error_response(response)
100
- end
101
- end
92
+ handle_response(response)
93
+ end
102
94
 
103
- def handle_success_response(response)
104
- if response.body.is_a?(Array)
105
- { success?: true, data: response.body }
106
- else
107
- response.body.merge(success?: true)
108
- end
109
- end
95
+ private
110
96
 
111
- def handle_error_response(response)
112
- case response.body
113
- when Hash
114
- { status_code: response.status, success?: false, error_code: response.body[:error], error_description: response.body[:error_description] }
115
- else
116
- { status_code: response&.status || 500, success?: false }
117
- end
118
- end
97
+ def handle_response(response)
98
+ if response.success?
99
+ handle_success_response(response)
100
+ else
101
+ handle_error_response(response)
102
+ end
119
103
  end
120
104
 
121
- methods = Authsignal.singleton_class.public_instance_methods(false)
122
- (methods - NON_API_METHODS).each do |method|
123
- define_singleton_method("#{method}!") do |*args, **kwargs|
124
- send(method, *args, **kwargs).tap do |response|
125
- status_code = response[:status_code]
126
- error_code = response[:error_code]
127
- error_description = response[:error_description]
105
+ def handle_success_response(response)
106
+ if response.body.is_a?(Array)
107
+ { success?: true, data: response.body }
108
+ else
109
+ response.body.merge(success?: true)
110
+ end
111
+ end
128
112
 
129
- raise ApiError.new(status_code, error_code, error_description) unless response[:success?]
130
- end
131
- end
113
+ def handle_error_response(response)
114
+ case response.body
115
+ when Hash
116
+ { status_code: response.status, success?: false, error_code: response.body[:error],
117
+ error_description: response.body[:error_description] }
118
+ else
119
+ { status_code: response&.status || 500, success?: false }
120
+ end
121
+ end
122
+ end
123
+
124
+ methods = Authsignal.singleton_class.public_instance_methods(false)
125
+ (methods - NON_API_METHODS).each do |method|
126
+ define_singleton_method("#{method}!") do |*args, **kwargs|
127
+ send(method, *args, **kwargs).tap do |response|
128
+ status_code = response[:status_code]
129
+ error_code = response[:error_code]
130
+ error_description = response[:error_description]
131
+
132
+ raise ApiError.new(status_code, error_code, error_description) unless response[:success?]
133
+ end
132
134
  end
135
+ end
133
136
  end
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: authsignal-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.1
4
+ version: 5.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - justinsoong
8
+ autorequire:
8
9
  bindir: exe
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-01-07 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: faraday
@@ -101,7 +102,9 @@ executables: []
101
102
  extensions: []
102
103
  extra_rdoc_files: []
103
104
  files:
105
+ - ".editorconfig"
104
106
  - ".rspec"
107
+ - ".rubocop.yml"
105
108
  - ".tool-versions"
106
109
  - CHANGELOG.md
107
110
  - Gemfile
@@ -127,6 +130,7 @@ licenses:
127
130
  metadata:
128
131
  homepage_uri: https://www.authsignal.com
129
132
  source_code_uri: https://github.com/authsignal/authsignal-ruby
133
+ post_install_message:
130
134
  rdoc_options: []
131
135
  require_paths:
132
136
  - lib
@@ -141,7 +145,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
145
  - !ruby/object:Gem::Version
142
146
  version: '0'
143
147
  requirements: []
144
- rubygems_version: 3.6.9
148
+ rubygems_version: 3.4.19
149
+ signing_key:
145
150
  specification_version: 4
146
151
  summary: The Authsignal ruby server side signal API.
147
152
  test_files: []