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 +4 -4
- data/lib/stytch/client.rb +1 -1
- data/lib/stytch/crypto_wallets.rb +2 -0
- data/lib/stytch/errors.rb +25 -0
- data/lib/stytch/magic_links.rb +2 -0
- data/lib/stytch/oauth.rb +2 -0
- data/lib/stytch/otps.rb +2 -0
- data/lib/stytch/sessions.rb +82 -5
- data/lib/stytch/totps.rb +4 -0
- data/lib/stytch/version.rb +1 -1
- data/lib/stytch/webauthn.rb +2 -0
- data/stytch.gemspec +2 -0
- metadata +36 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b507432445cb9a02301a537e29ffd6286dca4a9daa1629a6344bee6c073751e1
|
4
|
+
data.tar.gz: 48e01f84c824c94a987baf00221a142e45c5b5f976a25801cc04846f14658a7d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/stytch/magic_links.rb
CHANGED
@@ -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)
|
data/lib/stytch/sessions.rb
CHANGED
@@ -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)
|
data/lib/stytch/version.rb
CHANGED
data/lib/stytch/webauthn.rb
CHANGED
@@ -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
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.
|
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-
|
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
|
-
|
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.
|
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: []
|