stytch 6.4.0 → 6.5.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: 94327b3a992f3e596ec4c0a5a6bad97d2c78041e15f12a470949819aa5c0994d
4
- data.tar.gz: a02935d82c7129002058f425f15d73886b5f373b9f942e35cfb410fd62c7148d
3
+ metadata.gz: f0bc5efc1c2c0c36d794db592adba102628de2cd5cec77ffce94652b31e0007f
4
+ data.tar.gz: 689602f4ab5dc19c149c541eb3b15125d82ce515db9771583b8d6f22123eb577
5
5
  SHA512:
6
- metadata.gz: c366e0741c92ecf5ce5413beb57917a01f733ecc231540aebfab19c8a51ee75e7ee9deded5d23453b0580fc3556c90f7e2d1b707dfd79fa6aa6fa6b43cf5e152
7
- data.tar.gz: 9e2540999fa38c50436ea9409859cff663378d7b4a4d21a9ec15bdc8e36b326ed3ae2d681751739adbcecb57b3bd3a1c578394ba6d9dd2b92a1e9b5863f1a84b
6
+ metadata.gz: be5a989fee14537da3dac34ecea235509a9b1a1366fc1d9539c79ea600ea4b9abb9f9205faee9bfca0ad8ad07429d659d46af6f9b8815ac17343d8757fcc2098
7
+ data.tar.gz: e4f51e5f088df37798b52df974c376a52ebd3375270278b647b6e8d41e0355b5b1f4af53149cc1459eb5b7ca5b8f617bbe375e4ec57f55eb8819310662b09e9c
@@ -31,7 +31,7 @@ module StytchB2B
31
31
  @organizations = StytchB2B::Organizations.new(@connection)
32
32
  @passwords = StytchB2B::Passwords.new(@connection)
33
33
  @sso = StytchB2B::SSO.new(@connection)
34
- @sessions = StytchB2B::Sessions.new(@connection)
34
+ @sessions = StytchB2B::Sessions.new(@connection, project_id)
35
35
  end
36
36
 
37
37
  private
@@ -6,14 +6,31 @@
6
6
  # or your changes may be overwritten later!
7
7
  # !!!
8
8
 
9
+ require 'jwt'
10
+ require 'json/jwt'
11
+ require_relative 'errors'
9
12
  require_relative 'request_helper'
10
13
 
11
14
  module StytchB2B
12
15
  class Sessions
13
16
  include Stytch::RequestHelper
14
17
 
15
- def initialize(connection)
18
+ def initialize(connection, project_id)
16
19
  @connection = connection
20
+
21
+ @project_id = project_id
22
+ @cache_last_update = 0
23
+ @jwks_loader = lambda do |options|
24
+ @cached_keys = nil if options[:invalidate] && @cache_last_update < Time.now.to_i - 300
25
+ @cached_keys ||= begin
26
+ @cache_last_update = Time.now.to_i
27
+ keys = []
28
+ get_jwks(project_id: @project_id)['keys'].each do |r|
29
+ keys << r
30
+ end
31
+ { keys: keys }
32
+ end
33
+ end
17
34
  end
18
35
 
19
36
  # Retrieves all active Sessions for a Member.
@@ -290,5 +307,92 @@ module StytchB2B
290
307
  request = request_with_query_params("/v1/b2b/sessions/jwks/#{project_id}", query_params)
291
308
  get_request(request)
292
309
  end
310
+
311
+ # MANUAL(Sessions::authenticate_jwt)(SERVICE_METHOD)
312
+ # ADDIMPORT: require 'jwt'
313
+ # ADDIMPORT: require 'json/jwt'
314
+ # ADDIMPORT: require_relative 'errors'
315
+
316
+ # Parse a JWT and verify the signature. If max_token_age_seconds is unset, call the API directly
317
+ # If max_token_age_seconds is set and the JWT was issued (based on the "iat" claim) less than
318
+ # max_token_age_seconds seconds ago, then just verify locally and don't call the API
319
+ # To force remote validation for all tokens, set max_token_age_seconds to 0 or call authenticate()
320
+ def authenticate_jwt(
321
+ session_jwt,
322
+ max_token_age_seconds: nil,
323
+ session_duration_minutes: nil,
324
+ session_custom_claims: nil
325
+ )
326
+ if max_token_age_seconds == 0
327
+ return authenticate(
328
+ session_jwt: session_jwt,
329
+ session_duration_minutes: session_duration_minutes,
330
+ session_custom_claims: session_custom_claims
331
+ )
332
+ end
333
+
334
+ decoded_jwt = authenticate_jwt_local(session_jwt)
335
+ iat_time = Time.at(decoded_jwt['iat']).to_datetime
336
+ if iat_time + max_token_age_seconds >= Time.now
337
+ session = marshal_jwt_into_session(decoded_jwt)
338
+ { 'session' => session }
339
+ else
340
+ authenticate(
341
+ session_jwt: session_jwt,
342
+ session_duration_minutes: session_duration_minutes,
343
+ session_custom_claims: session_custom_claims
344
+ )
345
+ end
346
+ rescue StandardError
347
+ # JWT could not be verified locally. Check with the Stytch API.
348
+ authenticate(
349
+ session_jwt: session_jwt,
350
+ session_duration_minutes: session_duration_minutes,
351
+ session_custom_claims: session_custom_claims
352
+ )
353
+ end
354
+
355
+ # Parse a JWT and verify the signature locally (without calling /authenticate in the API)
356
+ # Uses the cached value to get the JWK but if it is unavailable, it calls the get_jwks()
357
+ # function to get the JWK
358
+ # This method never authenticates a JWT directly with the API
359
+ def authenticate_jwt_local(session_jwt)
360
+ issuer = 'stytch.com/' + @project_id
361
+ begin
362
+ decoded_token = JWT.decode session_jwt, nil, true,
363
+ { jwks: @jwks_loader, iss: issuer, verify_iss: true, aud: @project_id, verify_aud: true, algorithms: ['RS256'] }
364
+ decoded_token[0]
365
+ rescue JWT::InvalidIssuerError
366
+ raise JWTInvalidIssuerError
367
+ rescue JWT::InvalidAudError
368
+ raise JWTInvalidAudienceError
369
+ rescue JWT::ExpiredSignature
370
+ raise JWTExpiredSignatureError
371
+ rescue JWT::IncorrectAlgorithm
372
+ raise JWTIncorrectAlgorithmError
373
+ end
374
+ end
375
+
376
+ def marshal_jwt_into_session(jwt)
377
+ stytch_claim = 'https://stytch.com/session'
378
+ expires_at = jwt[stytch_claim]['expires_at'] || Time.at(jwt['exp']).to_datetime.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
379
+ # The custom claim set is all the claims in the payload except for the standard claims and
380
+ # the Stytch session claim. The cleanest way to collect those seems to be naming what we want
381
+ # to omit and filtering the rest to collect the custom claims.
382
+ reserved_claims = ['aud', 'exp', 'iat', 'iss', 'jti', 'nbf', 'sub', stytch_claim]
383
+ custom_claims = jwt.reject { |key, _| reserved_claims.include?(key) }
384
+ {
385
+ 'session_id' => jwt[stytch_claim]['id'],
386
+ 'user_id' => jwt['sub'],
387
+ 'started_at' => jwt[stytch_claim]['started_at'],
388
+ 'last_accessed_at' => jwt[stytch_claim]['last_accessed_at'],
389
+ # For JWTs that include it, prefer the inner expires_at claim.
390
+ 'expires_at' => expires_at,
391
+ 'attributes' => jwt[stytch_claim]['attributes'],
392
+ 'authentication_factors' => jwt[stytch_claim]['authentication_factors'],
393
+ 'custom_claims' => custom_claims
394
+ }
395
+ end
396
+ # ENDMANUAL(Sessions::authenticate_jwt)
293
397
  end
294
398
  end
@@ -167,9 +167,7 @@ module Stytch
167
167
  # Send a magic link to an existing Stytch user using their email address. If you'd like to create a user and send them a magic link by email with one request, use our [log in or create endpoint](https://stytch.com/docs/api/log-in-or-create-user-by-email).
168
168
  #
169
169
  # ### Add an email to an existing user
170
- # This endpoint also allows you to add a new email to an existing Stytch User. Including a `user_id`, `session_token`, or `session_jwt` in the request will add the email to the pre-existing Stytch User upon successful authentication.
171
- #
172
- # Adding a new email to an existing Stytch User requires the user to be present and validate the email via magic link. This requirement is in place to prevent account takeover attacks.
170
+ # This endpoint also allows you to add a new email address to an existing Stytch User. Including a `user_id`, `session_token`, or `session_jwt` in your Send Magic Link by email request will add the new, unverified email address to the existing Stytch User. Upon successful authentication, the email address will be marked as verified.
173
171
  #
174
172
  # ### Next steps
175
173
  # The user is emailed a magic link which redirects them to the provided [redirect URL](https://stytch.com/docs/guides/magic-links/email-magic-links/redirect-routing). Collect the `token` from the URL query parameters, and call [Authenticate magic link](https://stytch.com/docs/api/authenticate-magic-link) to complete authentication.
data/lib/stytch/otps.rb CHANGED
@@ -131,9 +131,7 @@ module Stytch
131
131
  #
132
132
  # ### Add a phone number to an existing user
133
133
  #
134
- # This endpoint also allows you to add a new phone number to an existing Stytch User. Including a `user_id`, `session_token`, or `session_jwt` in the request will add the phone number to the pre-existing Stytch User upon successful authentication.
135
- #
136
- # Adding a new phone number to an existing Stytch User requires the user to be present and validate the phone number via OTP. This requirement is in place to prevent account takeover attacks.
134
+ # This endpoint also allows you to add a new phone number to an existing Stytch User. Including a `user_id`, `session_token`, or `session_jwt` in your Send one-time passcode by SMS request will add the new, unverified phone number to the existing Stytch User. Upon successful authentication, the phone number will be marked as verified.
137
135
  #
138
136
  # ### Next steps
139
137
  #
@@ -289,9 +287,7 @@ module Stytch
289
287
  #
290
288
  # ### Add a phone number to an existing user
291
289
  #
292
- # This endpoint also allows you to add a new phone number to an existing Stytch User. Including a `user_id`, `session_token`, or `session_jwt` in the request will add the phone number to the pre-existing Stytch User upon successful authentication.
293
- #
294
- # Adding a new phone number to an existing Stytch User requires the user to be present and validate the phone number via OTP. This requirement is in place to prevent account takeover attacks.
290
+ # This endpoint also allows you to add a new phone number to an existing Stytch User. Including a `user_id`, `session_token`, or `session_jwt` in your Send one-time passcode by WhatsApp request will add the new, unverified phone number to the existing Stytch User. Upon successful authentication, the phone number will be marked as verified.
295
291
  #
296
292
  # ### Next steps
297
293
  #
@@ -442,9 +438,7 @@ module Stytch
442
438
  # Send a One-Time Passcode (OTP) to a User using their email. If you'd like to create a user and send them a passcode with one request, use our [log in or create endpoint](https://stytch.com/docs/api/log-in-or-create-user-by-email-otp).
443
439
  #
444
440
  # ### Add an email to an existing user
445
- # This endpoint also allows you to add a new email to an existing Stytch User. Including a `user_id`, `session_token`, or `session_jwt` in the request will add the email to the pre-existing Stytch User upon successful authentication.
446
- #
447
- # Adding a new email to an existing Stytch User requires the User to be present and validate the email via OTP. This requirement is in place to prevent account takeover attacks.
441
+ # This endpoint also allows you to add a new email address to an existing Stytch User. Including a `user_id`, `session_token`, or `session_jwt` in your Send one-time passcode by email request will add the new, unverified email address to the existing Stytch User. Upon successful authentication, the email address will be marked as verified.
448
442
  #
449
443
  # ### Next steps
450
444
  # Collect the OTP which was delivered to the user. Call [Authenticate OTP](https://stytch.com/docs/api/authenticate-otp) using the OTP `code` along with the `phone_id` found in the response as the `method_id`.
data/lib/stytch/users.rb CHANGED
@@ -196,7 +196,7 @@ module Stytch
196
196
 
197
197
  # Update a User's attributes.
198
198
  #
199
- # **Note:** In order to add a new email address or phone number to an existing User object, pass the new email address or phone number into the respective `/send` endpoint for the authentication method of your choice. If you specify the existing User's `user_id` while calling the `/send` endpoint, the new email address or phone number will be added to the existing User object upon successful authentication. We require this process to guard against an account takeover vulnerability.
199
+ # **Note:** In order to add a new email address or phone number to an existing User object, pass the new email address or phone number into the respective `/send` endpoint for the authentication method of your choice. If you specify the existing User's `user_id` while calling the `/send` endpoint, the new, unverified email address or phone number will be added to the existing User object. Upon successful authentication, the email address or phone number will be marked as verified. We require this process to guard against an account takeover vulnerability.
200
200
  #
201
201
  # == Parameters:
202
202
  # user_id::
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stytch
4
- VERSION = '6.4.0'
4
+ VERSION = '6.5.0'
5
5
  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: 6.4.0
4
+ version: 6.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - stytch
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-08-24 00:00:00.000000000 Z
11
+ date: 2023-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday