stytch 9.0.0 → 9.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5075518300c9fb8d82d98b80851825a1bfc72d61b637dbbbc54df07ad4b40f9
4
- data.tar.gz: e4abd2aec41c59f9d9605f0c6f43a62d5d4126991cfe89b5e96ad293a33922d5
3
+ metadata.gz: 833c91cba84e4cbbe5fd094bdf3d7ede5c0b351df7ebfab729fbaacdc181b14a
4
+ data.tar.gz: eccd3e064c0e2ba7bf0a7cbcc9f60b2aadaf25c0db70bee8c0c13646d30a6842
5
5
  SHA512:
6
- metadata.gz: 29a88d3485f4cabefe89721a83506e0dcbe64d8864780eec0508da70f5d308a204fa427cd0fb8e0c725c07bb45c20f37d182cb3f75e2694536f562b863a35ce9
7
- data.tar.gz: 6eaf332fea8ba9eb38da3af8d6eea7376b947a97e93f6478c0e149cc44d35236d9f148ee42e7179fb83ec6fa743076c806a7fc5b6c9e19e584763a3d2a5a2297
6
+ metadata.gz: c73c79869bb8e2bd29dac4307aa3dc4ba7b7a1c76eb1d5a780b29e2b62f2abcd2fe64244d654e37c99c160b8b5f1f6f762e02c59ea0510da034ebfb550c8d6ca
7
+ data.tar.gz: 07e99d0cd758a18aa37a20fe830ce7ba6ecfb19e9d6ec63a85870a12183b2da1ead1ec65315bee283d9f1b034f9f73be8b04aff9e5ff7fcd387867217cc92145
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  require:
2
- - rubocop-rspec
2
+ - rubocop-rspec
3
3
 
4
4
  AllCops:
5
5
  NewCops: disable
data/lib/stytch/errors.rb CHANGED
@@ -49,4 +49,11 @@ module Stytch
49
49
  super(msg)
50
50
  end
51
51
  end
52
+
53
+ class M2MPermissionError < StandardError
54
+ def initialize(has_scopes, required_scopes)
55
+ msg = "Missing at least one required scope from #{required_scopes} for M2M request with scopes #{has_scopes}"
56
+ super(msg)
57
+ end
58
+ end
52
59
  end
data/lib/stytch/m2m.rb CHANGED
@@ -96,6 +96,10 @@ module Stytch
96
96
  # max_token_age::
97
97
  # The maximum possible lifetime in seconds for the token to be valid.
98
98
  # The type of this field is nilable +Integer+.
99
+ # scope_authorization_func::
100
+ # A function to check if the token has the required scopes. This defaults to a function that assumes
101
+ # scopes are either direct string matches or written in the form "action:resource". See the
102
+ # documentation for +perform_authorization_check+ for more information.
99
103
  # == Returns:
100
104
  # +nil+ if the token could not be validated, or an object with the following fields:
101
105
  # scopes::
@@ -107,7 +111,12 @@ module Stytch
107
111
  # custom_claims::
108
112
  # A map of custom claims present in the token.
109
113
  # The type of this field is +object+.
110
- def authenticate_token(access_token:, required_scopes: nil, max_token_age: nil)
114
+ def authenticate_token(
115
+ access_token:,
116
+ required_scopes: nil,
117
+ max_token_age: nil,
118
+ scope_authorization_func: method(:perform_authorization_check)
119
+ )
111
120
  # Intentionally allow this to re-raise if authentication fails
112
121
  decoded_jwt = authenticate_token_local(access_token)
113
122
 
@@ -119,14 +128,50 @@ module Stytch
119
128
  resp = marshal_jwt_into_response(decoded_jwt)
120
129
 
121
130
  unless required_scopes.nil?
122
- for scope in required_scopes
123
- raise TokenMissingScopeError, scope unless resp['scopes'].include?(scope)
124
- end
131
+ is_authorized = scope_authorization_func.call(
132
+ has_scopes: resp['scopes'],
133
+ required_scopes: required_scopes
134
+ )
135
+ raise M2MPermissionError.new(resp['scopes'], required_scopes) unless is_authorized
125
136
  end
126
137
 
127
138
  resp
128
139
  end
129
140
 
141
+ # Performs an authorization check against an M2M client and a set of required
142
+ # scopes. Returns true if the client has all the required scopes, false otherwise.
143
+ # A scope can match if the client has a wildcard resource or the specific resource.
144
+ # This function assumes that scopes are of the form "action:resource" or just
145
+ # "specific_scope". It is _also_ possible to represent scopes as "resource:action",
146
+ # but it is ultimately up to the developer to ensure consistency in the scopes format.
147
+ # Note that a scope of "*" will only match another literal "*" because wildcards are
148
+ # *not* supported in the prefix piece of a scope.
149
+ def perform_authorization_check(
150
+ has_scopes:,
151
+ required_scopes:
152
+ )
153
+ client_scopes = Hash.new { |hash, key| hash[key] = Set.new }
154
+ has_scopes.each do |scope|
155
+ action = scope
156
+ resource = '-'
157
+ action, resource = scope.split(':') if scope.include?(':')
158
+ client_scopes[action].add(resource)
159
+ end
160
+
161
+ required_scopes.each do |required_scope|
162
+ required_action = required_scope
163
+ required_resource = '-'
164
+ required_action, required_resource = required_scope.split(':') if required_scope.include?(':')
165
+ return false unless client_scopes.key?(required_action)
166
+
167
+ resources = client_scopes[required_action]
168
+ # The client can either have a wildcard resource or the specific resource
169
+ return false unless resources.include?('*') || resources.include?(required_resource)
170
+ end
171
+
172
+ true
173
+ end
174
+
130
175
  # Parse a M2M token and verify the signature locally (without calling /authenticate in the API)
131
176
  def authenticate_token_local(jwt)
132
177
  issuer = 'stytch.com/' + @project_id
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stytch
4
- VERSION = '9.0.0'
4
+ VERSION = '9.1.0'
5
5
  end
data/stytch.gemspec CHANGED
@@ -30,6 +30,6 @@ Gem::Specification.new do |spec|
30
30
  spec.add_dependency 'jwt', '>= 2.3.0'
31
31
 
32
32
  spec.add_development_dependency 'rspec', '~> 3.11.0'
33
- spec.add_development_dependency 'rubocop', '1.56.3'
33
+ spec.add_development_dependency 'rubocop', '1.64.1'
34
34
  spec.add_development_dependency 'rubocop-rspec', '2.24.0'
35
35
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stytch
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.0.0
4
+ version: 9.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - stytch
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-06-03 00:00:00.000000000 Z
11
+ date: 2024-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -78,14 +78,14 @@ dependencies:
78
78
  requirements:
79
79
  - - '='
80
80
  - !ruby/object:Gem::Version
81
- version: 1.56.3
81
+ version: 1.64.1
82
82
  type: :development
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - '='
87
87
  - !ruby/object:Gem::Version
88
- version: 1.56.3
88
+ version: 1.64.1
89
89
  - !ruby/object:Gem::Dependency
90
90
  name: rubocop-rspec
91
91
  requirement: !ruby/object:Gem::Requirement