stytch 10.25.0 → 10.27.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2f2f709cf39fcc571c655a577f0846ef68c7767d4aeace2f2ea71f3d85781018
4
- data.tar.gz: afc3c7c5ce1dd327d2c5ab25f1b130eb25c355207bd8a15ac9db2c986b3f7eda
3
+ metadata.gz: 49aa54d3b047309a521fa267920e138f8eb27e8c299f5228c4c8bc565f821ce6
4
+ data.tar.gz: 716a18a6bcdc9ce4288287678e204d47ae90a89a7f432e43234afa2744419e04
5
5
  SHA512:
6
- metadata.gz: 9e1391f4ed96c281b6303f26532211f75332ce36fa012eba7629d2d2c6982b8fae6366778cff1b54a6b847e7893dac7926080e387893acb6d4efdd0a1029fb5c
7
- data.tar.gz: 94b8e29c50c8901bdb605e729de1440deaf73682d53e1da186862267a77bf7fc6545f63aec865fc25b61273da56d2c348b40d811e7b5690315158593bad5c49c
6
+ metadata.gz: 07cfbe4bbf895019aefd6be0d2fef3376caa5cb84fadd5c1af9c26d7d2e2ffd5399e5d9d3e2704cce5af85dc3eff494ad2ff648b1783434c933223c9408eafff
7
+ data.tar.gz: 55b223a9d0deabcc07810029395f1bd117c7df2b55db9b3e814e0dc8ce68e48737b2855b65888bb0f4039b14b90849acba473f3ab3961da61272e85198542c74
@@ -1,6 +1,13 @@
1
+ # !!!
2
+ # WARNING: This file is autogenerated
3
+ # Only modify code within MANUAL() sections
4
+ # or your changes may be overwritten later!
5
+ # !!!
6
+
1
7
  # frozen_string_literal: true
2
8
 
3
9
  require_relative 'b2b_discovery'
10
+ require_relative 'b2b_idp'
4
11
  require_relative 'b2b_impersonation'
5
12
  require_relative 'b2b_magic_links'
6
13
  require_relative 'b2b_oauth'
@@ -23,7 +30,7 @@ module StytchB2B
23
30
  class Client
24
31
  ENVIRONMENTS = %i[live test].freeze
25
32
 
26
- attr_reader :connected_app, :discovery, :fraud, :impersonation, :m2m, :magic_links, :oauth, :otps, :organizations, :passwords, :project, :rbac, :recovery_codes, :scim, :sso, :sessions, :totps
33
+ attr_reader :connected_app, :discovery, :fraud, :impersonation, :m2m, :magic_links, :oauth, :otps, :organizations, :passwords, :project, :rbac, :recovery_codes, :scim, :sso, :sessions, :totps, :idp
27
34
 
28
35
  def initialize(project_id:, secret:, env: nil, fraud_env: nil, &block)
29
36
  @api_host = api_host(env, project_id)
@@ -35,7 +42,8 @@ module StytchB2B
35
42
  create_connection(&block)
36
43
 
37
44
  rbac = StytchB2B::RBAC.new(@connection)
38
- @policy_cache = StytchB2B::PolicyCache.new(rbac_client: rbac)
45
+ @policy_cache = Stytch::PolicyCache.new(rbac_client: rbac)
46
+ @idp = StytchB2B::IDP.new(@connection, @project_id, @policy_cache)
39
47
 
40
48
  @connected_app = Stytch::ConnectedApp.new(@connection)
41
49
  @discovery = StytchB2B::Discovery.new(@connection)
@@ -48,7 +48,7 @@ module StytchB2B
48
48
  # The Intermediate Session Token. This token does not necessarily belong to a specific instance of a Member, but represents a bag of factors that may be converted to a member session. The token can be used with the [OTP SMS Authenticate endpoint](https://stytch.com/docs/b2b/api/authenticate-otp-sms), [TOTP Authenticate endpoint](https://stytch.com/docs/b2b/api/authenticate-totp), or [Recovery Codes Recover endpoint](https://stytch.com/docs/b2b/api/recovery-codes-recover) to complete an MFA flow and log in to the Organization. The token has a default expiry of 10 minutes. It can also be used with the [Exchange Intermediate Session endpoint](https://stytch.com/docs/b2b/api/exchange-intermediate-session) to join a specific Organization that allows the factors represented by the intermediate session token; or the [Create Organization via Discovery endpoint](https://stytch.com/docs/b2b/api/create-organization-via-discovery) to create a new Organization and Member. Intermediate Session Tokens have a default expiry of 10 minutes.
49
49
  # The type of this field is +String+.
50
50
  # organization_id::
51
- # Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug here as a convenience.
51
+ # Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
52
52
  # The type of this field is +String+.
53
53
  # session_duration_minutes::
54
54
  # Set the session lifetime to be this many minutes from now. This will start a new session if one doesn't already exist,
@@ -197,7 +197,7 @@ module StytchB2B
197
197
  # sso_jit_provisioning::
198
198
  # The authentication setting that controls the JIT provisioning of Members when authenticating via SSO. The accepted values are:
199
199
  #
200
- # `ALL_ALLOWED` – new Members will be automatically provisioned upon successful authentication via any of the Organization's `sso_active_connections`.
200
+ # `ALL_ALLOWED` – the default setting, new Members will be automatically provisioned upon successful authentication via any of the Organization's `sso_active_connections`.
201
201
  #
202
202
  # `RESTRICTED` – only new Members with SSO logins that comply with `sso_jit_provisioning_allowed_connections` can be provisioned upon authentication.
203
203
  #
@@ -215,7 +215,7 @@ module StytchB2B
215
215
  #
216
216
  # `RESTRICTED` – only new Members with verified emails that comply with `email_allowed_domains` can be provisioned upon authentication via Email Magic Link or OAuth.
217
217
  #
218
- # `NOT_ALLOWED` – disable JIT provisioning via Email Magic Link and OAuth.
218
+ # `NOT_ALLOWED` – the default setting, disables JIT provisioning via Email Magic Link and OAuth.
219
219
  #
220
220
  # The type of this field is nilable +String+.
221
221
  # email_invites::
@@ -273,7 +273,7 @@ module StytchB2B
273
273
  #
274
274
  # `RESTRICTED` – only new Members with tenants in `allowed_oauth_tenants` can JIT provision via tenant.
275
275
  #
276
- # `NOT_ALLOWED` – disable JIT provisioning by OAuth Tenant.
276
+ # `NOT_ALLOWED` – the default setting, disables JIT provisioning by OAuth Tenant.
277
277
  #
278
278
  # The type of this field is nilable +String+.
279
279
  # allowed_oauth_tenants::
@@ -282,7 +282,7 @@ module StytchB2B
282
282
  # first_party_connected_apps_allowed_type::
283
283
  # The authentication setting that sets the Organization's policy towards first party Connected Apps. The accepted values are:
284
284
  #
285
- # `ALL_ALLOWED` – any first party Connected App in the Project is permitted for use by Members.
285
+ # `ALL_ALLOWED` – the default setting, any first party Connected App in the Project is permitted for use by Members.
286
286
  #
287
287
  # `RESTRICTED` – only first party Connected Apps with IDs in `allowed_first_party_connected_apps` can be used by Members.
288
288
  #
@@ -295,7 +295,7 @@ module StytchB2B
295
295
  # third_party_connected_apps_allowed_type::
296
296
  # The authentication setting that sets the Organization's policy towards third party Connected Apps. The accepted values are:
297
297
  #
298
- # `ALL_ALLOWED` – any third party Connected App in the Project is permitted for use by Members.
298
+ # `ALL_ALLOWED` – the default setting, any third party Connected App in the Project is permitted for use by Members.
299
299
  #
300
300
  # `RESTRICTED` – only third party Connected Apps with IDs in `allowed_first_party_connected_apps` can be used by Members.
301
301
  #
@@ -0,0 +1,266 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jwt'
4
+ require 'json/jwt'
5
+ require_relative 'errors'
6
+ require_relative 'request_helper'
7
+ require_relative 'rbac_local'
8
+
9
+ module StytchB2B
10
+ class IDP
11
+ include Stytch::RequestHelper
12
+
13
+ def initialize(connection, project_id, policy_cache)
14
+ @connection = connection
15
+ @project_id = project_id
16
+ @policy_cache = policy_cache
17
+ @non_custom_claim_keys = [
18
+ 'aud',
19
+ 'exp',
20
+ 'iat',
21
+ 'iss',
22
+ 'jti',
23
+ 'nbf',
24
+ 'sub',
25
+ 'active',
26
+ 'client_id',
27
+ 'request_id',
28
+ 'scope',
29
+ 'status_code',
30
+ 'token_type',
31
+ 'https://stytch.com/organization'
32
+ ]
33
+ end
34
+
35
+ # Introspects a token JWT from an authorization code response.
36
+ # Access tokens are JWTs signed with the project's JWKs. Refresh tokens are opaque tokens.
37
+ # Access tokens contain a standard set of claims as well as any custom claims generated from templates.
38
+ #
39
+ # == Parameters:
40
+ # token::
41
+ # The access token (or refresh token) to introspect.
42
+ # The type of this field is +String+.
43
+ # client_id::
44
+ # The ID of the client.
45
+ # The type of this field is +String+.
46
+ # client_secret::
47
+ # The secret of the client.
48
+ # The type of this field is nilable +String+.
49
+ # token_type_hint::
50
+ # A hint on what the token contains. Valid fields are 'access_token' and 'refresh_token'.
51
+ # The type of this field is +String+.
52
+ # authorization_check::
53
+ # Optional authorization check object.
54
+ # The type of this field is nilable +Hash+.
55
+ #
56
+ # == Returns:
57
+ # An object with the following fields:
58
+ # subject::
59
+ # The subject of the token.
60
+ # The type of this field is +String+.
61
+ # scope::
62
+ # The scope of the token.
63
+ # The type of this field is +String+.
64
+ # audience::
65
+ # The audience of the token.
66
+ # The type of this field is +String+.
67
+ # expires_at::
68
+ # The expiration time of the token.
69
+ # The type of this field is +Integer+.
70
+ # issued_at::
71
+ # The issued at time of the token.
72
+ # The type of this field is +Integer+.
73
+ # issuer::
74
+ # The issuer of the token.
75
+ # The type of this field is +String+.
76
+ # not_before::
77
+ # The not before time of the token.
78
+ # The type of this field is +Integer+.
79
+ # token_type::
80
+ # The type of the token.
81
+ # The type of this field is +String+.
82
+ # custom_claims::
83
+ # Custom claims in the token.
84
+ # The type of this field is +Hash+.
85
+ # organization_claim::
86
+ # The organization claim in the token.
87
+ # The type of this field is +Hash+.
88
+ def introspect_token_network(
89
+ token:,
90
+ client_id:,
91
+ client_secret: nil,
92
+ token_type_hint: 'access_token',
93
+ authorization_check: nil
94
+ )
95
+ headers = {}
96
+ data = {
97
+ 'token' => token,
98
+ 'client_id' => client_id,
99
+ 'token_type_hint' => token_type_hint
100
+ }
101
+ data['client_secret'] = client_secret unless client_secret.nil?
102
+
103
+ url = @connection.url_prefix + '/v1/oauth2/introspect'
104
+ jwt_response = post_request(url, data, headers)
105
+
106
+ return nil unless jwt_response['active']
107
+
108
+ custom_claims = jwt_response.reject { |k, _| @non_custom_claim_keys.include?(k) }
109
+ organization_claim = jwt_response['https://stytch.com/organization']
110
+ organization_id = organization_claim['organization_id']
111
+ scope = jwt_response['scope']
112
+
113
+ if authorization_check
114
+ @policy_cache.perform_authorization_check(
115
+ subject_roles: scope.split,
116
+ authorization_check: authorization_check,
117
+ subject_org_id: organization_id
118
+ )
119
+ end
120
+
121
+ {
122
+ 'subject' => jwt_response['sub'],
123
+ 'scope' => jwt_response['scope'],
124
+ 'audience' => jwt_response['aud'],
125
+ 'expires_at' => jwt_response['exp'],
126
+ 'issued_at' => jwt_response['iat'],
127
+ 'issuer' => jwt_response['iss'],
128
+ 'not_before' => jwt_response['nbf'],
129
+ 'token_type' => jwt_response['token_type'],
130
+ 'custom_claims' => custom_claims,
131
+ 'organization_claim' => organization_claim
132
+ }
133
+ end
134
+
135
+ # Introspects a token JWT from an authorization code response.
136
+ # Access tokens are JWTs signed with the project's JWKs. Refresh tokens are opaque tokens.
137
+ # Access tokens contain a standard set of claims as well as any custom claims generated from templates.
138
+ #
139
+ # == Parameters:
140
+ # access_token::
141
+ # The access token (or refresh token) to introspect.
142
+ # The type of this field is +String+.
143
+ # authorization_check::
144
+ # Optional authorization check object.
145
+ # The type of this field is nilable +Hash+.
146
+ #
147
+ # == Returns:
148
+ # An object with the following fields:
149
+ # subject::
150
+ # The subject of the token.
151
+ # The type of this field is +String+.
152
+ # scope::
153
+ # The scope of the token.
154
+ # The type of this field is +String+.
155
+ # audience::
156
+ # The audience of the token.
157
+ # The type of this field is +String+.
158
+ # expires_at::
159
+ # The expiration time of the token.
160
+ # The type of this field is +Integer+.
161
+ # issued_at::
162
+ # The issued at time of the token.
163
+ # The type of this field is +Integer+.
164
+ # issuer::
165
+ # The issuer of the token.
166
+ # The type of this field is +String+.
167
+ # not_before::
168
+ # The not before time of the token.
169
+ # The type of this field is +Integer+.
170
+ # token_type::
171
+ # The type of the token.
172
+ # The type of this field is +String+.
173
+ # custom_claims::
174
+ # Custom claims in the token.
175
+ # The type of this field is +Hash+.
176
+ # organization_claim::
177
+ # The organization claim in the token.
178
+ # The type of this field is +Hash+.
179
+ def introspect_access_token_local(
180
+ access_token:,
181
+ authorization_check: nil
182
+ )
183
+ scope_claim = 'scope'
184
+ organization_claim = 'https://stytch.com/organization'
185
+
186
+ # Create a JWKS loader similar to other classes in the codebase
187
+ @cache_last_update = 0
188
+ jwks_loader = lambda do |options|
189
+ @cached_keys = nil if options[:invalidate] && @cache_last_update < Time.now.to_i - 300
190
+ if @cached_keys.nil?
191
+ @cached_keys = get_jwks(project_id: @project_id)
192
+ @cache_last_update = Time.now.to_i
193
+ end
194
+ @cached_keys
195
+ end
196
+
197
+ begin
198
+ decoded_jwt = JWT.decode(
199
+ access_token,
200
+ nil,
201
+ true,
202
+ {
203
+ algorithms: ['RS256'],
204
+ jwks: jwks_loader,
205
+ iss: ["stytch.com/#{@project_id}", @connection.url_prefix],
206
+ aud: @project_id
207
+ }
208
+ )[0]
209
+
210
+ generic_claims = decoded_jwt
211
+ custom_claims = generic_claims.reject { |k, _| @non_custom_claim_keys.include?(k) }
212
+ organization_claim_data = generic_claims[organization_claim]
213
+ organization_id = organization_claim_data['organization_id']
214
+ scope = generic_claims[scope_claim]
215
+
216
+ if authorization_check
217
+ @policy_cache.perform_authorization_check(
218
+ subject_roles: scope.split,
219
+ authorization_check: authorization_check,
220
+ subject_org_id: organization_id
221
+ )
222
+ end
223
+
224
+ {
225
+ 'subject' => generic_claims['sub'],
226
+ 'scope' => generic_claims[scope_claim],
227
+ 'audience' => generic_claims['aud'],
228
+ 'expires_at' => generic_claims['exp'],
229
+ 'issued_at' => generic_claims['iat'],
230
+ 'issuer' => generic_claims['iss'],
231
+ 'not_before' => generic_claims['nbf'],
232
+ 'token_type' => 'access_token',
233
+ 'custom_claims' => custom_claims,
234
+ 'organization_claim' => organization_claim_data
235
+ }
236
+ rescue JWT::InvalidIssuerError
237
+ raise Stytch::JWTInvalidIssuerError
238
+ rescue JWT::InvalidAudError
239
+ raise Stytch::JWTInvalidAudienceError
240
+ rescue JWT::ExpiredSignature
241
+ raise Stytch::JWTExpiredSignatureError
242
+ rescue JWT::IncorrectAlgorithm
243
+ raise Stytch::JWTIncorrectAlgorithmError
244
+ rescue JWT::DecodeError
245
+ nil
246
+ end
247
+ end
248
+
249
+ # Gets the JWKS for the project.
250
+ #
251
+ # == Parameters:
252
+ # project_id::
253
+ # The ID of the project.
254
+ # The type of this field is +String+.
255
+ #
256
+ # == Returns:
257
+ # The JWKS for the project.
258
+ # The type of this field is +Hash+.
259
+ def get_jwks(project_id:)
260
+ headers = {}
261
+ query_params = {}
262
+ request = request_with_query_params("/v1/b2b/sessions/jwks/#{project_id}", query_params)
263
+ get_request(request, headers)
264
+ end
265
+ end
266
+ end
@@ -20,7 +20,7 @@ module StytchB2B
20
20
  @discovery = StytchB2B::MagicLinks::Discovery.new(@connection)
21
21
  end
22
22
 
23
- # Authenticate a Member with a Magic Link. This endpoint requires a Magic Link token that is not expired or previously used. If the Members status is `pending` or `invited`, they will be updated to `active`.
23
+ # Authenticate a Member with a Magic Link. This endpoint requires a Magic Link token that is not expired or previously used. If the Member's status is `pending` or `invited`, they will be updated to `active`.
24
24
  # Provide the `session_duration_minutes` parameter to set the lifetime of the session. If the `session_duration_minutes` parameter is not specified, a Stytch session will be created with a 60 minute duration.
25
25
  #
26
26
  # If the Member is required to complete MFA to log in to the Organization, the returned value of `member_authenticated` will be `false`, and an `intermediate_session_token` will be returned.
@@ -187,7 +187,7 @@ module StytchB2B
187
187
  #
188
188
  # == Parameters:
189
189
  # organization_id::
190
- # Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug here as a convenience.
190
+ # Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
191
191
  # The type of this field is +String+.
192
192
  # email_address::
193
193
  # The email address of the Member.
@@ -287,7 +287,7 @@ module StytchB2B
287
287
  #
288
288
  # == Parameters:
289
289
  # organization_id::
290
- # Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug here as a convenience.
290
+ # Globally unique UUID that identifies a specific Organization. The `organization_id` is critical to perform operations on an Organization, so be sure to preserve this value. You may also use the organization_slug or organization_external_id here as a convenience.
291
291
  # The type of this field is +String+.
292
292
  # email_address::
293
293
  # The email address of the Member.
@@ -31,7 +31,7 @@ module StytchB2B
31
31
  # If the Member is logging in via an OAuth provider that does not fully verify the email, the returned value of `member_authenticated` will be `false`, and an `intermediate_session_token` will be returned.
32
32
  # The `primary_required` field details the authentication flow the Member must perform in order to [complete a step-up authentication](https://stytch.com/docs/b2b/guides/oauth/auth-flows) into the organization. The `intermediate_session_token` must be passed into that authentication flow.
33
33
  #
34
- # Were actively accepting requests for new OAuth providers! Please [email us](mailto:support@stytch.com) or [post in our community](https://stytch.com/docs/b2b/resources) if you are looking for an OAuth provider that is not currently supported.
34
+ # We're actively accepting requests for new OAuth providers! Please [email us](mailto:support@stytch.com) or [post in our community](https://stytch.com/docs/b2b/resources) if you are looking for an OAuth provider that is not currently supported.
35
35
  #
36
36
  # == Parameters:
37
37
  # oauth_token::