stytch 2.11.0 → 2.12.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: d9e1c109c231f2dedb872cf4528ad44366517c5bac06adff7139b355e09c0537
4
- data.tar.gz: 1cfd8aa59c94ca621cda81a36a2669ff1352a84919d0ebe304aec47d6463a989
3
+ metadata.gz: b507432445cb9a02301a537e29ffd6286dca4a9daa1629a6344bee6c073751e1
4
+ data.tar.gz: 48e01f84c824c94a987baf00221a142e45c5b5f976a25801cc04846f14658a7d
5
5
  SHA512:
6
- metadata.gz: 21f8bd406c9f5e9135cb93e6f66f1bf7f3b9a4644b6a2e3309afa688302ce466b6cc343d871df5bc295fea07bcf20862a4ccdf0ea8d5eb1df7621a79cb86b6c1
7
- data.tar.gz: 2f7bf4ae7d789858a36d65dea80a9ecd1c6f459748fb301d9eda97e2ce015a125a97f3bd9dda381ec0709d2e962ba268734736ce2aee0dc5e751ace10d7b9f13
6
+ metadata.gz: '093757bb72ff4b992e94e875df54c7b781a8c3d1bbf54c5f3be79f3d8df00b0ec7a70cc25a0477977122c6f498533cc47881e0ad2b51fa1fb352a89e23f35387'
7
+ data.tar.gz: a53dcc14f57a8a141f048232c004b886c3b2ad291c70fd0ebd595b883e2301f0e96a52146a5255a359ecb8cb1204dd92c50b2e54c1412cf2185b533e50d97a1c
data/lib/stytch/client.rb CHANGED
@@ -26,7 +26,7 @@ module Stytch
26
26
  @magic_links = Stytch::MagicLinks.new(@connection)
27
27
  @oauth = Stytch::OAuth.new(@connection)
28
28
  @otps = Stytch::OTPs.new(@connection)
29
- @sessions = Stytch::Sessions.new(@connection)
29
+ @sessions = Stytch::Sessions.new(@connection, @project_id)
30
30
  @totps = Stytch::TOTPs.new(@connection)
31
31
  @webauthn = Stytch::WebAuthn.new(@connection)
32
32
  @crypto_wallets = Stytch::CryptoWallets.new(@connection)
@@ -32,6 +32,7 @@ module Stytch
32
32
  crypto_wallet_type:,
33
33
  signature:,
34
34
  session_token: nil,
35
+ session_jwt: nil,
35
36
  session_duration_minutes: nil
36
37
  )
37
38
  request = {
@@ -41,6 +42,7 @@ module Stytch
41
42
  }
42
43
 
43
44
  request[:session_token] = session_token unless session_token.nil?
45
+ request[:session_jwt] = session_jwt unless session_jwt.nil?
44
46
  request[:session_duration_minutes] = session_duration_minutes unless session_duration_minutes.nil?
45
47
 
46
48
  post_request("#{PATH}/authenticate", request)
@@ -0,0 +1,25 @@
1
+ module Stytch
2
+ class JWTInvalidIssuerError < StandardError
3
+ def initialize(msg="JWT issuer did not match")
4
+ super
5
+ end
6
+ end
7
+
8
+ class JWTInvalidAudienceError < StandardError
9
+ def initialize(msg="JWT audience did not match")
10
+ super
11
+ end
12
+ end
13
+
14
+ class JWTExpiredSignatureError < StandardError
15
+ def initialize(msg="JWT signature has expired")
16
+ super
17
+ end
18
+ end
19
+
20
+ class JWTIncorrectAlgorithmError < StandardError
21
+ def initialize(msg="JWT algorithm is incorrect")
22
+ super
23
+ end
24
+ end
25
+ end
@@ -36,6 +36,7 @@ module Stytch
36
36
  attributes: {},
37
37
  options: {},
38
38
  session_token: nil,
39
+ session_jwt: nil,
39
40
  session_duration_minutes: nil
40
41
  )
41
42
  request = {
@@ -45,6 +46,7 @@ module Stytch
45
46
  request[:attributes] = attributes if attributes != {}
46
47
  request[:options] = options if options != {}
47
48
  request[:session_token] = session_token unless session_token.nil?
49
+ request[:session_jwt] = session_jwt unless session_jwt.nil?
48
50
  request[:session_duration_minutes] = session_duration_minutes unless session_duration_minutes.nil?
49
51
 
50
52
  post_request("#{PATH}/authenticate", request)
data/lib/stytch/oauth.rb CHANGED
@@ -16,6 +16,7 @@ module Stytch
16
16
  token:,
17
17
  session_management_type: nil,
18
18
  session_token: nil,
19
+ session_jwt: nil,
19
20
  session_duration_minutes: nil
20
21
  )
21
22
  request = {
@@ -23,6 +24,7 @@ module Stytch
23
24
  }
24
25
  request[:session_management_type] = session_management_type unless session_management_type.nil?
25
26
  request[:session_token] = session_token unless session_token.nil?
27
+ request[:session_jwt] = session_jwt unless session_jwt.nil?
26
28
  request[:session_duration_minutes] = session_duration_minutes unless session_duration_minutes.nil?
27
29
 
28
30
  post_request("#{PATH}/authenticate", request)
data/lib/stytch/otps.rb CHANGED
@@ -24,6 +24,7 @@ module Stytch
24
24
  attributes: {},
25
25
  options: {},
26
26
  session_token: nil,
27
+ session_jwt: nil,
27
28
  session_duration_minutes: nil
28
29
  )
29
30
  request = {
@@ -34,6 +35,7 @@ module Stytch
34
35
  request[:attributes] = attributes if attributes != {}
35
36
  request[:options] = options if options != {}
36
37
  request[:session_token] = session_token unless session_token.nil?
38
+ request[:session_jwt] = session_jwt unless session_jwt.nil?
37
39
  request[:session_duration_minutes] = session_duration_minutes unless session_duration_minutes.nil?
38
40
 
39
41
  post_request("#{PATH}/authenticate", request)
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'jwt'
4
+ require 'json/jwt'
5
+
6
+ require_relative 'errors'
3
7
  require_relative 'request_helper'
4
8
 
5
9
  module Stytch
@@ -8,8 +12,12 @@ module Stytch
8
12
 
9
13
  PATH = '/v1/sessions'
10
14
 
11
- def initialize(connection)
15
+ def initialize(connection, project_id)
12
16
  @connection = connection
17
+ @project_id = project_id
18
+ @jwks_loader = ->(options) do
19
+ options[:invalidate] ? jwks(project_id: @project_id) : {}
20
+ end
13
21
  end
14
22
 
15
23
  def get(user_id:)
@@ -23,13 +31,14 @@ module Stytch
23
31
  end
24
32
 
25
33
  def authenticate(
26
- session_token:,
34
+ session_token: nil,
35
+ session_jwt: nil,
27
36
  session_duration_minutes: nil
28
37
  )
29
- request = {
30
- session_token: session_token
31
- }
38
+ request = {}
32
39
 
40
+ request[:session_token] = session_token unless session_token.nil?
41
+ request[:session_jwt] = session_jwt unless session_jwt.nil?
33
42
  request[:session_duration_minutes] = session_duration_minutes unless session_duration_minutes.nil?
34
43
 
35
44
  post_request("#{PATH}/authenticate", request)
@@ -46,5 +55,73 @@ module Stytch
46
55
 
47
56
  post_request("#{PATH}/revoke", request)
48
57
  end
58
+
59
+ def jwks(project_id:)
60
+ request_path = "#{PATH}/jwks/" + project_id
61
+ get_request(request_path)
62
+ end
63
+
64
+ # Parse a JWT and verify the signature. If max_token_age_seconds is unset, call the API directly
65
+ # If max_token_age_seconds is set and the JWT was issued (based on the "iat" claim) less than
66
+ # max_token_age_seconds seconds ago, then just verify locally and don't call the API
67
+ # To force remote validation for all tokens, set max_token_age_seconds to 0 or call authenticate()
68
+ def authenticate_jwt(
69
+ session_jwt,
70
+ max_token_age_seconds: nil,
71
+ session_duration_minutes: nil
72
+ )
73
+ if max_token_age_seconds == 0
74
+ return authenticate(
75
+ session_jwt: session_jwt,
76
+ session_duration_minutes: session_duration_minutes,
77
+ )
78
+ end
79
+
80
+ decoded_jwt = authenticate_jwt_local(session_jwt)
81
+ iat_time = Time.at(decoded_jwt["iat"]).to_datetime
82
+ if iat_time + max_token_age_seconds >= Time.now
83
+ session = marshal_jwt_into_session(decoded_jwt)
84
+ return {"session" => session}
85
+ else
86
+ return authenticate(
87
+ session_jwt: session_jwt,
88
+ session_duration_minutes: session_duration_minutes,
89
+ )
90
+ end
91
+ end
92
+
93
+ # Parse a JWT and verify the signature locally (without calling /authenticate in the API)
94
+ # Uses the cached value to get the JWK but if it is unavailable, it calls the get_jwks()
95
+ # function to get the JWK
96
+ # This method never authenticates a JWT directly with the API
97
+ def authenticate_jwt_local(session_jwt)
98
+ issuer = "stytch.com/" + @project_id
99
+ begin
100
+ decoded_token = JWT.decode session_jwt, nil, true,
101
+ { jwks: @jwks_loader, iss: issuer, verify_iss: true, aud: @project_id, verify_aud: true, algorithms: ["RS256"]}
102
+ return decoded_token[0]
103
+ rescue JWT::InvalidIssuerError
104
+ raise JWTInvalidIssuerError
105
+ rescue JWT::InvalidAudError
106
+ raise JWTInvalidAudienceError
107
+ rescue JWT::ExpiredSignature
108
+ raise JWTExpiredSignatureError
109
+ rescue JWT::IncorrectAlgorithm
110
+ raise JWTIncorrectAlgorithmError
111
+ end
112
+ end
113
+
114
+ def marshal_jwt_into_session(jwt)
115
+ stytch_claim = "https://stytch.com/session"
116
+ return {
117
+ "session_id" => jwt["jti"],
118
+ "user_id" => jwt["sub"],
119
+ "started_at" => jwt[stytch_claim]["started_at"],
120
+ "last_accessed_at" => jwt[stytch_claim]["last_accessed_at"],
121
+ "expires_at" => Time.at(jwt["exp"]).to_datetime.iso8601,
122
+ "attributes" => jwt[stytch_claim]["attributes"],
123
+ "authentication_factors" => jwt[stytch_claim]["authentication_factors"],
124
+ }
125
+ end
49
126
  end
50
127
  end
data/lib/stytch/totps.rb CHANGED
@@ -29,6 +29,7 @@ module Stytch
29
29
  user_id:,
30
30
  totp_code:,
31
31
  session_token: nil,
32
+ session_jwt: nil,
32
33
  session_duration_minutes: nil
33
34
  )
34
35
  request = {
@@ -37,6 +38,7 @@ module Stytch
37
38
  }
38
39
 
39
40
  request[:session_token] = session_token unless session_token.nil?
41
+ request[:session_jwt] = session_jwt unless session_jwt.nil?
40
42
  request[:session_duration_minutes] = session_duration_minutes unless session_duration_minutes.nil?
41
43
 
42
44
  post_request("#{PATH}/authenticate", request)
@@ -56,6 +58,7 @@ module Stytch
56
58
  user_id:,
57
59
  recovery_code:,
58
60
  session_token: nil,
61
+ session_jwt: nil,
59
62
  session_duration_minutes: nil
60
63
  )
61
64
  request = {
@@ -64,6 +67,7 @@ module Stytch
64
67
  }
65
68
 
66
69
  request[:session_token] = session_token unless session_token.nil?
70
+ request[:session_jwt] = session_jwt unless session_jwt.nil?
67
71
  request[:session_duration_minutes] = session_duration_minutes unless session_duration_minutes.nil?
68
72
 
69
73
  post_request("#{PATH}/recover", request)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Stytch
4
- VERSION = '2.11.0'
4
+ VERSION = '2.12.0'
5
5
  end
@@ -56,6 +56,7 @@ module Stytch
56
56
  def authenticate(
57
57
  public_key_credential:,
58
58
  session_token: nil,
59
+ session_jwt: nil,
59
60
  session_duration_minutes: nil
60
61
  )
61
62
  request = {
@@ -63,6 +64,7 @@ module Stytch
63
64
  }
64
65
 
65
66
  request[:session_token] = session_token unless session_token.nil?
67
+ request[:session_jwt] = session_jwt unless session_jwt.nil?
66
68
  request[:session_duration_minutes] = session_duration_minutes unless session_duration_minutes.nil?
67
69
 
68
70
  post_request("#{PATH}/authenticate", request)
data/stytch.gemspec CHANGED
@@ -27,4 +27,6 @@ Gem::Specification.new do |spec|
27
27
 
28
28
  spec.add_dependency 'faraday', '>= 0.17.0', '< 2.0'
29
29
  spec.add_dependency 'faraday_middleware', '>= 0.14.0', '< 2.0'
30
+ spec.add_dependency 'jwt', '>= 2.3.0'
31
+ spec.add_dependency 'json-jwt', '>=1.13.0'
30
32
  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: 2.11.0
4
+ version: 2.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - stytch
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-08 00:00:00.000000000 Z
11
+ date: 2022-04-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -50,7 +50,35 @@ dependencies:
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
52
  version: '2.0'
53
- description:
53
+ - !ruby/object:Gem::Dependency
54
+ name: jwt
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: 2.3.0
60
+ type: :runtime
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 2.3.0
67
+ - !ruby/object:Gem::Dependency
68
+ name: json-jwt
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 1.13.0
74
+ type: :runtime
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 1.13.0
81
+ description:
54
82
  email:
55
83
  - support@stytch.com
56
84
  executables: []
@@ -72,6 +100,7 @@ files:
72
100
  - lib/stytch.rb
73
101
  - lib/stytch/client.rb
74
102
  - lib/stytch/crypto_wallets.rb
103
+ - lib/stytch/errors.rb
75
104
  - lib/stytch/magic_links.rb
76
105
  - lib/stytch/middleware.rb
77
106
  - lib/stytch/oauth.rb
@@ -89,7 +118,7 @@ licenses:
89
118
  metadata:
90
119
  homepage_uri: https://stytch.com
91
120
  source_code_uri: https://github.com/stytchauth/stytch-ruby
92
- post_install_message:
121
+ post_install_message:
93
122
  rdoc_options: []
94
123
  require_paths:
95
124
  - lib
@@ -104,8 +133,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
133
  - !ruby/object:Gem::Version
105
134
  version: '0'
106
135
  requirements: []
107
- rubygems_version: 3.1.4
108
- signing_key:
136
+ rubygems_version: 3.0.3
137
+ signing_key:
109
138
  specification_version: 4
110
139
  summary: Stytch Ruby Gem
111
140
  test_files: []