authsignal-ruby 5.2.1 → 5.3.0
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 +4 -4
- data/.editorconfig +19 -0
- data/.rubocop.yml +50 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -2
- data/Gemfile.lock +50 -32
- data/Rakefile +3 -3
- data/bin/console +3 -3
- data/lib/authsignal/api_error.rb +1 -1
- data/lib/authsignal/client.rb +254 -112
- data/lib/authsignal/configuration.rb +51 -48
- data/lib/authsignal/invalid_signature_error.rb +2 -3
- data/lib/authsignal/middleware/json_request.rb +1 -3
- data/lib/authsignal/middleware/json_response.rb +5 -4
- data/lib/authsignal/version.rb +1 -1
- data/lib/authsignal/webhook.rb +20 -23
- data/lib/authsignal.rb +168 -99
- metadata +9 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bf8ccfc3244f86d9e99c7c481d5c23fb9f341aad018e371522f3755908e9bc9d
|
|
4
|
+
data.tar.gz: c6b7cc9b3c50fcc043ffdd73cbd14dd7114fa8b7977f654f1ade619444061b9d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c83bb4ab1de9b24b12820ebe82328741150e7f3821e954588f79df4b8e8b2d4ccfc3b259c36a0b86d23424940e0dda04107b8dfd33bb0e2fee9514bed41a3f2a
|
|
7
|
+
data.tar.gz: 97b80dcf590ec5f8f0ffa196e908669dcfdefaf5ea90a8228e2378add33d522a565488373c8ac8884936f3119c91965c48ea2aa7df5d1f003aa309694399d846
|
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,50 @@
|
|
|
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/modules can be longer
|
|
26
|
+
Metrics/ClassLength:
|
|
27
|
+
Max: 250
|
|
28
|
+
|
|
29
|
+
Metrics/ModuleLength:
|
|
30
|
+
Max: 150
|
|
31
|
+
|
|
32
|
+
Metrics/MethodLength:
|
|
33
|
+
Max: 25
|
|
34
|
+
|
|
35
|
+
Metrics/AbcSize:
|
|
36
|
+
Max: 30
|
|
37
|
+
|
|
38
|
+
# Skip documentation requirement for SDK
|
|
39
|
+
Style/Documentation:
|
|
40
|
+
Enabled: false
|
|
41
|
+
|
|
42
|
+
# Gemspec specific
|
|
43
|
+
Gemspec/DevelopmentDependencies:
|
|
44
|
+
Enabled: false
|
|
45
|
+
|
|
46
|
+
Gemspec/RequireMFA:
|
|
47
|
+
Enabled: false
|
|
48
|
+
|
|
49
|
+
Gemspec/OrderedDependencies:
|
|
50
|
+
Enabled: false
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.6.10
|
data/Gemfile
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
source
|
|
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.
|
|
4
|
+
authsignal-ruby (5.3.0)
|
|
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.
|
|
13
|
-
public_suffix (>= 2.0.2, <
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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.
|
|
20
|
-
dotenv (
|
|
21
|
-
faraday (2.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
faraday-net_http (3.
|
|
26
|
-
|
|
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.
|
|
30
|
-
json (2.7.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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.
|
|
45
|
+
rspec-core (3.13.6)
|
|
42
46
|
rspec-support (~> 3.13.0)
|
|
43
|
-
rspec-expectations (3.13.
|
|
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.
|
|
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.
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
86
|
+
1.17.2
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
require
|
|
5
|
-
require
|
|
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
|
|
14
|
+
require 'irb'
|
|
15
15
|
IRB.start(__FILE__)
|
data/lib/authsignal/api_error.rb
CHANGED
|
@@ -25,7 +25,7 @@ module Authsignal
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def format_description(error_code, error_description)
|
|
28
|
-
error_description
|
|
28
|
+
error_description&.length&.positive? ? error_description : error_code
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
end
|
data/lib/authsignal/client.rb
CHANGED
|
@@ -1,117 +1,259 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'erb'
|
|
2
4
|
|
|
3
5
|
module Authsignal
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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 query_users(
|
|
56
|
+
username: nil,
|
|
57
|
+
email: nil,
|
|
58
|
+
phone_number: nil,
|
|
59
|
+
token: nil,
|
|
60
|
+
limit: nil,
|
|
61
|
+
last_evaluated_user_id: nil
|
|
62
|
+
)
|
|
63
|
+
params = {
|
|
64
|
+
username: username,
|
|
65
|
+
email: email,
|
|
66
|
+
phoneNumber: phone_number,
|
|
67
|
+
token: token,
|
|
68
|
+
limit: limit&.to_s,
|
|
69
|
+
lastEvaluatedUserId: last_evaluated_user_id
|
|
70
|
+
}.compact
|
|
71
|
+
|
|
72
|
+
path = params.empty? ? 'users' : "users?#{URI.encode_www_form(params)}"
|
|
73
|
+
make_request(:get, path)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def get_authenticators(user_id:)
|
|
77
|
+
make_request(:get, "users/#{url_encode(user_id)}/authenticators")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def enroll_verified_authenticator(user_id:, attributes:)
|
|
81
|
+
make_request(:post, "users/#{url_encode(user_id)}/authenticators", body: attributes)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def delete_authenticator(user_id:, user_authenticator_id:)
|
|
85
|
+
make_request(:delete, "users/#{url_encode(user_id)}/authenticators/#{url_encode(user_authenticator_id)}")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def track(user_id:, action:, attributes:)
|
|
89
|
+
path = "users/#{user_id}/actions/#{action}"
|
|
90
|
+
|
|
91
|
+
make_request(:post, path, body: attributes)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def validate_challenge(token:, user_id: nil, action: nil)
|
|
95
|
+
path = 'validate'
|
|
96
|
+
body = { user_id: user_id, token: token, action: action }
|
|
97
|
+
|
|
98
|
+
make_request(:post, path, body: body)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def get_action(user_id:, action:, idempotency_key:)
|
|
102
|
+
make_request(:get, "users/#{url_encode(user_id)}/actions/#{action}/#{url_encode(idempotency_key)}")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def query_user_actions(
|
|
106
|
+
user_id:,
|
|
107
|
+
from_date: nil,
|
|
108
|
+
action_codes: [],
|
|
109
|
+
state: nil
|
|
110
|
+
)
|
|
111
|
+
params = {
|
|
112
|
+
fromDate: from_date,
|
|
113
|
+
codes: action_codes.empty? ? nil : action_codes.join(','),
|
|
114
|
+
state: state
|
|
115
|
+
}.compact
|
|
116
|
+
|
|
117
|
+
base_path = "users/#{url_encode(user_id)}/actions"
|
|
118
|
+
path = params.empty? ? base_path : "#{base_path}?#{URI.encode_www_form(params)}"
|
|
119
|
+
make_request(:get, path)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def update_action(user_id:, action:, idempotency_key:, attributes:)
|
|
123
|
+
make_request(:patch, "users/#{url_encode(user_id)}/actions/#{action}/#{url_encode(idempotency_key)}", body: attributes)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def challenge(
|
|
127
|
+
verification_method:,
|
|
128
|
+
action:,
|
|
129
|
+
idempotency_key: nil,
|
|
130
|
+
user_id: nil,
|
|
131
|
+
email: nil,
|
|
132
|
+
phone_number: nil,
|
|
133
|
+
sms_channel: nil,
|
|
134
|
+
locale: nil,
|
|
135
|
+
device_id: nil,
|
|
136
|
+
ip_address: nil,
|
|
137
|
+
user_agent: nil,
|
|
138
|
+
custom: nil,
|
|
139
|
+
scope: nil
|
|
140
|
+
)
|
|
141
|
+
body = {
|
|
142
|
+
verification_method: verification_method,
|
|
143
|
+
action: action,
|
|
144
|
+
idempotency_key: idempotency_key,
|
|
145
|
+
user_id: user_id,
|
|
146
|
+
email: email,
|
|
147
|
+
phone_number: phone_number,
|
|
148
|
+
sms_channel: sms_channel,
|
|
149
|
+
locale: locale,
|
|
150
|
+
device_id: device_id,
|
|
151
|
+
ip_address: ip_address,
|
|
152
|
+
user_agent: user_agent,
|
|
153
|
+
custom: custom,
|
|
154
|
+
scope: scope
|
|
155
|
+
}
|
|
156
|
+
make_request(:post, 'challenge', body: body)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def verify(challenge_id:, verification_code:)
|
|
160
|
+
body = {
|
|
161
|
+
challenge_id: challenge_id,
|
|
162
|
+
verification_code: verification_code
|
|
163
|
+
}
|
|
164
|
+
make_request(:post, 'verify', body: body)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def claim_challenge(
|
|
168
|
+
challenge_id:,
|
|
169
|
+
user_id:,
|
|
170
|
+
skip_verification_check: nil
|
|
171
|
+
)
|
|
172
|
+
body = {
|
|
173
|
+
challenge_id: challenge_id,
|
|
174
|
+
user_id: user_id,
|
|
175
|
+
skip_verification_check: skip_verification_check
|
|
176
|
+
}
|
|
177
|
+
make_request(:post, 'claim', body: body)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def get_challenge(
|
|
181
|
+
challenge_id: nil,
|
|
182
|
+
user_id: nil,
|
|
183
|
+
action: nil,
|
|
184
|
+
verification_method: nil
|
|
185
|
+
)
|
|
186
|
+
params = {}
|
|
187
|
+
params[:challengeId] = challenge_id if challenge_id
|
|
188
|
+
params[:userId] = user_id if user_id
|
|
189
|
+
params[:action] = action if action
|
|
190
|
+
params[:verificationMethod] = verification_method if verification_method
|
|
191
|
+
|
|
192
|
+
query_string = URI.encode_www_form(params) unless params.empty?
|
|
193
|
+
path = query_string ? "challenges?#{query_string}" : 'challenges'
|
|
194
|
+
|
|
195
|
+
make_request(:get, path)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def create_session(client_id:, token:, action: nil)
|
|
199
|
+
body = {
|
|
200
|
+
client_id: client_id,
|
|
201
|
+
token: token,
|
|
202
|
+
action: action
|
|
203
|
+
}.compact
|
|
204
|
+
make_request(:post, 'sessions', body: body)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def validate_session(access_token:, client_ids: nil)
|
|
208
|
+
body = {
|
|
209
|
+
access_token: access_token,
|
|
210
|
+
client_ids: client_ids
|
|
211
|
+
}.compact
|
|
212
|
+
make_request(:post, 'sessions/validate', body: body)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def refresh_session(refresh_token:)
|
|
216
|
+
body = { refresh_token: refresh_token }
|
|
217
|
+
make_request(:post, 'sessions/refresh', body: body)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def revoke_session(access_token:)
|
|
221
|
+
body = { access_token: access_token }
|
|
222
|
+
make_request(:post, 'sessions/revoke', body: body)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def revoke_user_sessions(user_id:)
|
|
226
|
+
body = { user_id: user_id }
|
|
227
|
+
make_request(:post, 'sessions/user/revoke', body: body)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
##
|
|
231
|
+
# TODO: delete identify?
|
|
232
|
+
def identify(user_id, user_payload)
|
|
233
|
+
make_request(:post, "users/#{url_encode(user_id)}", body: user_payload)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
private
|
|
237
|
+
|
|
238
|
+
def url_encode(str)
|
|
239
|
+
ERB::Util.url_encode(str)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def version
|
|
243
|
+
Authsignal.configuration.version
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def print_api_key_warning
|
|
247
|
+
warn(NO_API_KEY_MESSAGE)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def require_api_key
|
|
251
|
+
Authsignal.configuration.api_secret_key || print_api_key_warning
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def make_request(method, path, body: nil, headers: nil)
|
|
255
|
+
body = body.compact if body.is_a?(Hash)
|
|
256
|
+
@client.public_send(method, path, body, headers)
|
|
116
257
|
end
|
|
258
|
+
end
|
|
117
259
|
end
|
|
@@ -1,52 +1,55 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Authsignal
|
|
4
4
|
class Configuration
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
|
@@ -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(
|
|
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
|
-
|
|
24
|
-
word.gsub
|
|
25
|
-
word.
|
|
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
|
data/lib/authsignal/version.rb
CHANGED
data/lib/authsignal/webhook.rb
CHANGED
|
@@ -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 =
|
|
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
|
|
23
|
-
raise InvalidSignatureError,
|
|
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
|
-
|
|
53
|
-
timestamp: -1,
|
|
54
|
-
signatures: []
|
|
55
|
-
}
|
|
52
|
+
handle_invalid_signature unless value
|
|
56
53
|
|
|
57
|
-
|
|
54
|
+
result = extract_signature_parts(value)
|
|
55
|
+
handle_invalid_signature if result[:timestamp] == -1 || result[:signatures].empty?
|
|
58
56
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
next unless kv.length == 2
|
|
57
|
+
result
|
|
58
|
+
end
|
|
62
59
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
71
|
-
|
|
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,
|
|
77
|
+
raise InvalidSignatureError, 'Signature format is invalid.'
|
|
81
78
|
end
|
|
82
79
|
end
|
|
83
80
|
end
|
data/lib/authsignal.rb
CHANGED
|
@@ -1,133 +1,202 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
-
require
|
|
10
|
-
require
|
|
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
|
-
|
|
15
|
+
NON_API_METHODS = %i[setup configuration default_configuration webhook].freeze
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
class << self
|
|
18
|
+
attr_writer :configuration
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
def setup
|
|
21
|
+
yield(configuration)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def configuration
|
|
25
|
+
@configuration ||= Authsignal::Configuration.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def default_configuration
|
|
29
|
+
configuration.defaults
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def webhook
|
|
33
|
+
@webhook ||= Webhook.new(configuration.api_secret_key)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def get_user(user_id:)
|
|
37
|
+
response = Client.new.get_user(user_id: user_id)
|
|
38
|
+
|
|
39
|
+
handle_response(response)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def query_users(**options)
|
|
43
|
+
response = Client.new.query_users(**options)
|
|
44
|
+
|
|
45
|
+
handle_response(response)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def update_user(user_id:, attributes:)
|
|
49
|
+
response = Client.new.update_user(user_id: user_id, attributes: attributes)
|
|
50
|
+
|
|
51
|
+
handle_response(response)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def delete_user(user_id:)
|
|
55
|
+
response = Client.new.delete_user(user_id: user_id)
|
|
56
|
+
|
|
57
|
+
handle_response(response)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def get_authenticators(user_id:)
|
|
61
|
+
response = Client.new.get_authenticators(user_id: user_id)
|
|
62
|
+
|
|
63
|
+
handle_response(response)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def enroll_verified_authenticator(user_id:, attributes:)
|
|
67
|
+
response = Client.new.enroll_verified_authenticator(user_id: user_id, attributes: attributes)
|
|
68
|
+
|
|
69
|
+
handle_response(response)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def delete_authenticator(user_id:, user_authenticator_id:)
|
|
73
|
+
response = Client.new.delete_authenticator(user_id: user_id, user_authenticator_id: user_authenticator_id)
|
|
74
|
+
|
|
75
|
+
handle_response(response)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def track(user_id:, action:, attributes:)
|
|
79
|
+
response = Client.new.track(user_id: user_id, action: action, attributes: attributes)
|
|
80
|
+
handle_response(response)
|
|
81
|
+
end
|
|
21
82
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
end
|
|
83
|
+
def validate_challenge(token:, user_id: nil, action: nil)
|
|
84
|
+
response = Client.new.validate_challenge(token: token, user_id: user_id, action: action)
|
|
25
85
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
86
|
+
handle_response(response)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def get_action(user_id:, action:, idempotency_key:)
|
|
90
|
+
response = Client.new.get_action(user_id: user_id, action: action, idempotency_key: idempotency_key)
|
|
91
|
+
|
|
92
|
+
handle_response(response)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def query_user_actions(user_id:, **options)
|
|
96
|
+
response = Client.new.query_user_actions(user_id: user_id, **options)
|
|
29
97
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
end
|
|
98
|
+
handle_response(response)
|
|
99
|
+
end
|
|
33
100
|
|
|
34
|
-
|
|
35
|
-
|
|
101
|
+
def update_action(user_id:, action:, idempotency_key:, attributes:)
|
|
102
|
+
response = Client.new.update_action(user_id: user_id, action: action, idempotency_key: idempotency_key, attributes: attributes)
|
|
36
103
|
|
|
37
|
-
|
|
38
|
-
|
|
104
|
+
handle_response(response)
|
|
105
|
+
end
|
|
39
106
|
|
|
40
|
-
|
|
41
|
-
|
|
107
|
+
def challenge(verification_method:, action:, **options)
|
|
108
|
+
response = Client.new.challenge(verification_method: verification_method, action: action, **options)
|
|
42
109
|
|
|
43
|
-
|
|
44
|
-
|
|
110
|
+
handle_response(response)
|
|
111
|
+
end
|
|
45
112
|
|
|
46
|
-
|
|
47
|
-
|
|
113
|
+
def verify(challenge_id:, verification_code:)
|
|
114
|
+
response = Client.new.verify(challenge_id: challenge_id, verification_code: verification_code)
|
|
48
115
|
|
|
49
|
-
|
|
50
|
-
|
|
116
|
+
handle_response(response)
|
|
117
|
+
end
|
|
51
118
|
|
|
52
|
-
|
|
53
|
-
|
|
119
|
+
def claim_challenge(challenge_id:, user_id:, **options)
|
|
120
|
+
response = Client.new.claim_challenge(challenge_id: challenge_id, user_id: user_id, **options)
|
|
54
121
|
|
|
55
|
-
|
|
56
|
-
|
|
122
|
+
handle_response(response)
|
|
123
|
+
end
|
|
57
124
|
|
|
58
|
-
|
|
59
|
-
|
|
125
|
+
def get_challenge(**options)
|
|
126
|
+
response = Client.new.get_challenge(**options)
|
|
60
127
|
|
|
61
|
-
|
|
62
|
-
|
|
128
|
+
handle_response(response)
|
|
129
|
+
end
|
|
63
130
|
|
|
64
|
-
|
|
65
|
-
|
|
131
|
+
def create_session(client_id:, token:, action: nil)
|
|
132
|
+
response = Client.new.create_session(client_id: client_id, token: token, action: action)
|
|
66
133
|
|
|
67
|
-
|
|
68
|
-
|
|
134
|
+
handle_response(response)
|
|
135
|
+
end
|
|
69
136
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
handle_response(response)
|
|
73
|
-
end
|
|
137
|
+
def validate_session(access_token:, client_ids: nil)
|
|
138
|
+
response = Client.new.validate_session(access_token: access_token, client_ids: client_ids)
|
|
74
139
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
handle_response(response)
|
|
79
|
-
end
|
|
140
|
+
handle_response(response)
|
|
141
|
+
end
|
|
80
142
|
|
|
81
|
-
|
|
82
|
-
|
|
143
|
+
def refresh_session(refresh_token:)
|
|
144
|
+
response = Client.new.refresh_session(refresh_token: refresh_token)
|
|
83
145
|
|
|
84
|
-
|
|
85
|
-
|
|
146
|
+
handle_response(response)
|
|
147
|
+
end
|
|
86
148
|
|
|
87
|
-
|
|
88
|
-
|
|
149
|
+
def revoke_session(access_token:)
|
|
150
|
+
response = Client.new.revoke_session(access_token: access_token)
|
|
89
151
|
|
|
90
|
-
|
|
91
|
-
|
|
152
|
+
handle_response(response)
|
|
153
|
+
end
|
|
92
154
|
|
|
93
|
-
|
|
155
|
+
def revoke_user_sessions(user_id:)
|
|
156
|
+
response = Client.new.revoke_user_sessions(user_id: user_id)
|
|
94
157
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
handle_success_response(response)
|
|
98
|
-
else
|
|
99
|
-
handle_error_response(response)
|
|
100
|
-
end
|
|
101
|
-
end
|
|
158
|
+
handle_response(response)
|
|
159
|
+
end
|
|
102
160
|
|
|
103
|
-
|
|
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
|
|
161
|
+
private
|
|
110
162
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
end
|
|
118
|
-
end
|
|
163
|
+
def handle_response(response)
|
|
164
|
+
if response.success?
|
|
165
|
+
handle_success_response(response)
|
|
166
|
+
else
|
|
167
|
+
handle_error_response(response)
|
|
168
|
+
end
|
|
119
169
|
end
|
|
120
170
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
171
|
+
def handle_success_response(response)
|
|
172
|
+
if response.body.is_a?(Array)
|
|
173
|
+
{ success?: true, data: response.body }
|
|
174
|
+
else
|
|
175
|
+
response.body.merge(success?: true)
|
|
176
|
+
end
|
|
177
|
+
end
|
|
128
178
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
179
|
+
def handle_error_response(response)
|
|
180
|
+
case response.body
|
|
181
|
+
when Hash
|
|
182
|
+
{ status_code: response.status, success?: false, error_code: response.body[:error],
|
|
183
|
+
error_description: response.body[:error_description] }
|
|
184
|
+
else
|
|
185
|
+
{ status_code: response&.status || 500, success?: false }
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
methods = Authsignal.singleton_class.public_instance_methods(false)
|
|
191
|
+
(methods - NON_API_METHODS).each do |method|
|
|
192
|
+
define_singleton_method("#{method}!") do |*args, **kwargs|
|
|
193
|
+
send(method, *args, **kwargs).tap do |response|
|
|
194
|
+
status_code = response[:status_code]
|
|
195
|
+
error_code = response[:error_code]
|
|
196
|
+
error_description = response[:error_description]
|
|
197
|
+
|
|
198
|
+
raise ApiError.new(status_code, error_code, error_description) unless response[:success?]
|
|
199
|
+
end
|
|
132
200
|
end
|
|
201
|
+
end
|
|
133
202
|
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.
|
|
4
|
+
version: 5.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- justinsoong
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: exe
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2026-01-08 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: faraday
|
|
@@ -101,7 +102,10 @@ executables: []
|
|
|
101
102
|
extensions: []
|
|
102
103
|
extra_rdoc_files: []
|
|
103
104
|
files:
|
|
105
|
+
- ".editorconfig"
|
|
104
106
|
- ".rspec"
|
|
107
|
+
- ".rubocop.yml"
|
|
108
|
+
- ".ruby-version"
|
|
105
109
|
- ".tool-versions"
|
|
106
110
|
- CHANGELOG.md
|
|
107
111
|
- Gemfile
|
|
@@ -127,6 +131,7 @@ licenses:
|
|
|
127
131
|
metadata:
|
|
128
132
|
homepage_uri: https://www.authsignal.com
|
|
129
133
|
source_code_uri: https://github.com/authsignal/authsignal-ruby
|
|
134
|
+
post_install_message:
|
|
130
135
|
rdoc_options: []
|
|
131
136
|
require_paths:
|
|
132
137
|
- lib
|
|
@@ -141,7 +146,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
141
146
|
- !ruby/object:Gem::Version
|
|
142
147
|
version: '0'
|
|
143
148
|
requirements: []
|
|
144
|
-
rubygems_version: 3.
|
|
149
|
+
rubygems_version: 3.4.19
|
|
150
|
+
signing_key:
|
|
145
151
|
specification_version: 4
|
|
146
152
|
summary: The Authsignal ruby server side signal API.
|
|
147
153
|
test_files: []
|