stytch 2.10.1 → 3.0.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/README.md +3 -4
- data/lib/stytch/client.rb +4 -2
- data/lib/stytch/crypto_wallets.rb +51 -0
- data/lib/stytch/errors.rb +25 -0
- data/lib/stytch/magic_links.rb +2 -0
- data/lib/stytch/oauth.rb +2 -2
- data/lib/stytch/otps.rb +2 -0
- data/lib/stytch/sessions.rb +82 -5
- data/lib/stytch/totps.rb +4 -0
- data/lib/stytch/users.rb +9 -1
- data/lib/stytch/version.rb +1 -1
- data/lib/stytch/webauthn.rb +2 -0
- data/stytch.gemspec +2 -0
- metadata +33 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a361942c6f469a3bd03cb666b2740717d4f163ae5668536d292e4c72670b2a3
|
4
|
+
data.tar.gz: 865893e5e873829cee7fc43cb9aa9a006fa87487ccf04cc2c41009e657f407e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ab5a3c953f0c7eea8297685e97e408271f00389b563de2ac8583fec5b5c2a95971b00920a42c87232f584afad6fcfdfab6ef09b541a5d35d5df809771d9e925
|
7
|
+
data.tar.gz: 274ee8bf41f5cdc48d5d35c5f97e368d4766cb5670c3294afc8f8f5364e9835540d07eb7f02d804414602f8cbca777c6dae42110a4db657fd655289bb649a766
|
data/README.md
CHANGED
@@ -34,6 +34,7 @@ This client library supports all of Stytch's live products:
|
|
34
34
|
- [x] [Session Management](https://stytch.com/docs/api/sessions-overview)
|
35
35
|
- [x] [WebAuthn (Beta)](https://stytch.com/docs/api/webauthn-overview)
|
36
36
|
- [x] [Time-based one-time passcodes (TOTPs) (Beta)](https://stytch.com/docs/api/totps-overview)
|
37
|
+
- [x] [Crypto wallets (Beta)](https://stytch.com/docs/api/crypto-wallet-overview)
|
37
38
|
|
38
39
|
### Example usage
|
39
40
|
Create an API client:
|
@@ -48,16 +49,14 @@ client = Stytch::Client.new(
|
|
48
49
|
Send a magic link by email:
|
49
50
|
```ruby
|
50
51
|
client.magic_links.email.login_or_create(
|
51
|
-
email: "sandbox@stytch.com"
|
52
|
-
login_magic_link_url: "https://example.com/login",
|
53
|
-
signup_magic_link_url: "https://example.com/signup",
|
52
|
+
email: "sandbox@stytch.com"
|
54
53
|
)
|
55
54
|
```
|
56
55
|
|
57
56
|
Authenticate the token from the magic link:
|
58
57
|
```ruby
|
59
58
|
client.magic_links.authenticate(
|
60
|
-
token: "SeiGwdj5lKkrEVgcEY3QNJXt6srxS3IK2Nwkar6mXD4="
|
59
|
+
token: "SeiGwdj5lKkrEVgcEY3QNJXt6srxS3IK2Nwkar6mXD4="
|
61
60
|
)
|
62
61
|
```
|
63
62
|
|
data/lib/stytch/client.rb
CHANGED
@@ -7,12 +7,13 @@ require_relative 'otps'
|
|
7
7
|
require_relative 'sessions'
|
8
8
|
require_relative 'totps'
|
9
9
|
require_relative 'webauthn'
|
10
|
+
require_relative 'crypto_wallets'
|
10
11
|
|
11
12
|
module Stytch
|
12
13
|
class Client
|
13
14
|
ENVIRONMENTS = %i[live test].freeze
|
14
15
|
|
15
|
-
attr_reader :users, :magic_links, :oauth, :otps, :sessions, :totps, :webauthn
|
16
|
+
attr_reader :users, :magic_links, :oauth, :otps, :sessions, :totps, :webauthn, :crypto_wallets
|
16
17
|
|
17
18
|
def initialize(env:, project_id:, secret:, &block)
|
18
19
|
@api_host = api_host(env)
|
@@ -25,9 +26,10 @@ module Stytch
|
|
25
26
|
@magic_links = Stytch::MagicLinks.new(@connection)
|
26
27
|
@oauth = Stytch::OAuth.new(@connection)
|
27
28
|
@otps = Stytch::OTPs.new(@connection)
|
28
|
-
@sessions = Stytch::Sessions.new(@connection)
|
29
|
+
@sessions = Stytch::Sessions.new(@connection, @project_id)
|
29
30
|
@totps = Stytch::TOTPs.new(@connection)
|
30
31
|
@webauthn = Stytch::WebAuthn.new(@connection)
|
32
|
+
@crypto_wallets = Stytch::CryptoWallets.new(@connection)
|
31
33
|
end
|
32
34
|
|
33
35
|
private
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'request_helper'
|
4
|
+
|
5
|
+
module Stytch
|
6
|
+
class CryptoWallets
|
7
|
+
include Stytch::RequestHelper
|
8
|
+
|
9
|
+
PATH = '/v1/crypto_wallets'
|
10
|
+
|
11
|
+
def initialize(connection)
|
12
|
+
@connection = connection
|
13
|
+
end
|
14
|
+
|
15
|
+
def authenticate_start(
|
16
|
+
crypto_wallet_address:,
|
17
|
+
crypto_wallet_type:,
|
18
|
+
user_id: nil
|
19
|
+
)
|
20
|
+
request = {
|
21
|
+
crypto_wallet_address: crypto_wallet_address,
|
22
|
+
crypto_wallet_type: crypto_wallet_type
|
23
|
+
}
|
24
|
+
|
25
|
+
request[:user_id] = user_id unless user_id.nil?
|
26
|
+
|
27
|
+
post_request("#{PATH}/authenticate/start", request)
|
28
|
+
end
|
29
|
+
|
30
|
+
def authenticate(
|
31
|
+
crypto_wallet_address:,
|
32
|
+
crypto_wallet_type:,
|
33
|
+
signature:,
|
34
|
+
session_token: nil,
|
35
|
+
session_jwt: nil,
|
36
|
+
session_duration_minutes: nil
|
37
|
+
)
|
38
|
+
request = {
|
39
|
+
crypto_wallet_address: crypto_wallet_address,
|
40
|
+
crypto_wallet_type: crypto_wallet_type,
|
41
|
+
signature: signature
|
42
|
+
}
|
43
|
+
|
44
|
+
request[:session_token] = session_token unless session_token.nil?
|
45
|
+
request[:session_jwt] = session_jwt unless session_jwt.nil?
|
46
|
+
request[:session_duration_minutes] = session_duration_minutes unless session_duration_minutes.nil?
|
47
|
+
|
48
|
+
post_request("#{PATH}/authenticate", request)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -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
@@ -14,15 +14,15 @@ module Stytch
|
|
14
14
|
|
15
15
|
def authenticate(
|
16
16
|
token:,
|
17
|
-
session_management_type: nil,
|
18
17
|
session_token: nil,
|
18
|
+
session_jwt: nil,
|
19
19
|
session_duration_minutes: nil
|
20
20
|
)
|
21
21
|
request = {
|
22
22
|
token: token
|
23
23
|
}
|
24
|
-
request[:session_management_type] = session_management_type unless session_management_type.nil?
|
25
24
|
request[:session_token] = session_token unless session_token.nil?
|
25
|
+
request[:session_jwt] = session_jwt unless session_jwt.nil?
|
26
26
|
request[:session_duration_minutes] = session_duration_minutes unless session_duration_minutes.nil?
|
27
27
|
|
28
28
|
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/users.rb
CHANGED
@@ -54,11 +54,13 @@ module Stytch
|
|
54
54
|
name: {},
|
55
55
|
emails: [],
|
56
56
|
phone_numbers: [],
|
57
|
+
crypto_wallets: [],
|
57
58
|
attributes: {}
|
58
59
|
)
|
59
60
|
request = {
|
60
61
|
emails: format_emails(emails),
|
61
|
-
phone_numbers: format_phone_numbers(phone_numbers)
|
62
|
+
phone_numbers: format_phone_numbers(phone_numbers),
|
63
|
+
crypto_wallets: crypto_wallets
|
62
64
|
}
|
63
65
|
|
64
66
|
request[:name] = name if name != {}
|
@@ -95,6 +97,12 @@ module Stytch
|
|
95
97
|
delete_request("#{PATH}/totps/#{totp_id}")
|
96
98
|
end
|
97
99
|
|
100
|
+
def delete_crypto_wallet(
|
101
|
+
crypto_wallet_id:
|
102
|
+
)
|
103
|
+
delete_request("#{PATH}/crypto_wallets/#{crypto_wallet_id}")
|
104
|
+
end
|
105
|
+
|
98
106
|
private
|
99
107
|
|
100
108
|
def format_emails(emails)
|
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:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- stytch
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -50,6 +50,34 @@ dependencies:
|
|
50
50
|
- - "<"
|
51
51
|
- !ruby/object:Gem::Version
|
52
52
|
version: '2.0'
|
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
|
53
81
|
description:
|
54
82
|
email:
|
55
83
|
- support@stytch.com
|
@@ -71,6 +99,8 @@ files:
|
|
71
99
|
- bin/setup
|
72
100
|
- lib/stytch.rb
|
73
101
|
- lib/stytch/client.rb
|
102
|
+
- lib/stytch/crypto_wallets.rb
|
103
|
+
- lib/stytch/errors.rb
|
74
104
|
- lib/stytch/magic_links.rb
|
75
105
|
- lib/stytch/middleware.rb
|
76
106
|
- lib/stytch/oauth.rb
|
@@ -103,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
133
|
- !ruby/object:Gem::Version
|
104
134
|
version: '0'
|
105
135
|
requirements: []
|
106
|
-
rubygems_version: 3.0.3
|
136
|
+
rubygems_version: 3.0.3.1
|
107
137
|
signing_key:
|
108
138
|
specification_version: 4
|
109
139
|
summary: Stytch Ruby Gem
|