simplicity_client 0.2.2 → 0.2.3
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/simplicity_client.rb +303 -303
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 875f444b4107a2ef0e2955c96bacb84a74b9a02089a8d72917fb4708e2110ce5
|
4
|
+
data.tar.gz: 6a6ba44bc27f552b42e97da6b0ff1c1a18e07ff334839107a3c276b376c23e8c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d9ea9df4d09f765b05758fdeac3ca34f8ae18c8217601195383bf6e1aa8e09bf0faaf12d49e838adc500d4b1eb2bf597299b317c1ebe3613b717bbd25e72fac
|
7
|
+
data.tar.gz: e04a5050f749fa4840ab95e34b85c5f320200a1d8df5cf52965d7dc13d2149899a5014eacbe9cd985e5eb57bd495846d1eb9b10e7395ad9fc25a03e8222207bb
|
data/lib/simplicity_client.rb
CHANGED
@@ -1,303 +1,303 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
begin
|
4
|
-
require "dotenv/load"
|
5
|
-
rescue LoadError
|
6
|
-
# Dotenv is not available, so move on without loading it. It's only used for development.
|
7
|
-
end
|
8
|
-
require "faraday"
|
9
|
-
require "faraday-cookie_jar"
|
10
|
-
require "faraday/follow_redirects"
|
11
|
-
require "aws-sdk-cognitoidentityprovider"
|
12
|
-
require "aws-sdk-cognitoidentity"
|
13
|
-
require "aws-sigv4"
|
14
|
-
require_relative "fido2_client"
|
15
|
-
|
16
|
-
module SimplicityClient
|
17
|
-
Params = Struct.new(:email, :credentialId, :keyAlgorithm, :keyCurve, :keyValue, :userHandle, keyword_init: true)
|
18
|
-
ParamDefinition = Struct.new(:name, :label, :primary_id)
|
19
|
-
|
20
|
-
class Error < StandardError; end
|
21
|
-
|
22
|
-
# For the user interface
|
23
|
-
PARAMS_INFO = [
|
24
|
-
ParamDefinition.new(name: "email", label: "Email address", primary_id: true),
|
25
|
-
ParamDefinition.new(name: "credentialId", label: "Passkey credential ID"),
|
26
|
-
ParamDefinition.new(name: "keyAlgorithm", label: "Passkey key algorithm"),
|
27
|
-
ParamDefinition.new(name: "keyCurve", label: "Passkey key curve"),
|
28
|
-
ParamDefinition.new(name: "keyValue", label: "Passkey value"),
|
29
|
-
ParamDefinition.new(name: "userHandle", label: "Passkey user handle")
|
30
|
-
].freeze
|
31
|
-
|
32
|
-
class Session
|
33
|
-
def initialize
|
34
|
-
@logger = Logger.new $stderr
|
35
|
-
@logger.level = Logger::DEBUG
|
36
|
-
|
37
|
-
@user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"
|
38
|
-
|
39
|
-
@client = Faraday.new(
|
40
|
-
headers: { "User-Agent" => @user_agent },
|
41
|
-
# proxy: "http://Thinkbook.local:8080",
|
42
|
-
# :ssl => {:verify => false}
|
43
|
-
) do |builder|
|
44
|
-
builder.response :follow_redirects
|
45
|
-
builder.response :logger
|
46
|
-
builder.adapter Faraday.default_adapter
|
47
|
-
end
|
48
|
-
|
49
|
-
@authsignal_base = "https://au.api.authsignal.com/v1"
|
50
|
-
@authsignal_tenant_id = "e768822f-b1a1-404c-a685-0e37905ca5f5"
|
51
|
-
end
|
52
|
-
|
53
|
-
# Write the session data out to a string so that the session can be restored later
|
54
|
-
def export
|
55
|
-
{
|
56
|
-
auth_data: @auth_data,
|
57
|
-
}.to_json
|
58
|
-
end
|
59
|
-
|
60
|
-
# Load the session data from a string to restore a session
|
61
|
-
def load(str)
|
62
|
-
json = JSON.parse(str)
|
63
|
-
@auth_data = json["auth_data"]
|
64
|
-
end
|
65
|
-
|
66
|
-
def login(params)
|
67
|
-
client = Aws::CognitoIdentityProvider::Client.new(region: "ap-southeast-2")
|
68
|
-
|
69
|
-
user_pool_id = "ap-southeast-2_abAklW6ap"
|
70
|
-
|
71
|
-
device_id = SecureRandom.uuid
|
72
|
-
passkey = Fido2Client::Passkey.new(params.credentialId, params.keyAlgorithm, params.keyCurve, params.keyValue, params.userHandle)
|
73
|
-
challenge_id = fetch_challenge_id
|
74
|
-
authentication_options = fetch_authentication_options(challenge_id)
|
75
|
-
challenge = authentication_options["options"]["challenge"]
|
76
|
-
assertion = Fido2Client::Client.new.get_assertion(passkey, challenge)
|
77
|
-
passkey_result = present_passkey(challenge_id, assertion, device_id)
|
78
|
-
|
79
|
-
resp = client.initiate_auth(
|
80
|
-
{
|
81
|
-
auth_flow: "CUSTOM_AUTH",
|
82
|
-
auth_parameters: {
|
83
|
-
USERNAME: params.email,
|
84
|
-
},
|
85
|
-
client_metadata: {
|
86
|
-
anonymousId: device_id,
|
87
|
-
},
|
88
|
-
client_id: "kvoiu7unft0c8hqqsa6hkmeu5",
|
89
|
-
},
|
90
|
-
)
|
91
|
-
|
92
|
-
resp = client.respond_to_auth_challenge(
|
93
|
-
{
|
94
|
-
challenge_name: "CUSTOM_CHALLENGE",
|
95
|
-
challenge_responses: {
|
96
|
-
USERNAME: params.email,
|
97
|
-
ANSWER: passkey_result["accessToken"],
|
98
|
-
},
|
99
|
-
client_id: "kvoiu7unft0c8hqqsa6hkmeu5",
|
100
|
-
session: resp.session,
|
101
|
-
},
|
102
|
-
)
|
103
|
-
|
104
|
-
cognito_identity_client = Aws::CognitoIdentity::Client.new(region: "ap-southeast-2")
|
105
|
-
|
106
|
-
# Assuming you have the Identity Pool ID and the ID token
|
107
|
-
identity_pool_id = "ap-southeast-2:0ed33fc6-4cef-4f2e-b634-31c616e108e2"
|
108
|
-
id_token = resp.authentication_result.id_token
|
109
|
-
|
110
|
-
# Get ID from the identity pool
|
111
|
-
id_response = cognito_identity_client.get_id(
|
112
|
-
{
|
113
|
-
identity_pool_id: identity_pool_id,
|
114
|
-
logins: {
|
115
|
-
"cognito-idp.ap-southeast-2.amazonaws.com/#{user_pool_id}" => id_token,
|
116
|
-
},
|
117
|
-
},
|
118
|
-
)
|
119
|
-
|
120
|
-
# Get credentials for the ID
|
121
|
-
credentials_response = cognito_identity_client.get_credentials_for_identity(
|
122
|
-
{
|
123
|
-
identity_id: id_response.identity_id,
|
124
|
-
logins: {
|
125
|
-
"cognito-idp.ap-southeast-2.amazonaws.com/#{user_pool_id}" => id_token,
|
126
|
-
},
|
127
|
-
},
|
128
|
-
)
|
129
|
-
|
130
|
-
access_key_id = credentials_response.credentials.access_key_id
|
131
|
-
secret_key = credentials_response.credentials.secret_key
|
132
|
-
session_token = credentials_response.credentials.session_token
|
133
|
-
|
134
|
-
@auth_data = {
|
135
|
-
email: params.email,
|
136
|
-
access_key_id: access_key_id,
|
137
|
-
secret_key: secret_key,
|
138
|
-
region: "ap-southeast-2",
|
139
|
-
session_token: session_token,
|
140
|
-
}.transform_keys(&:to_s)
|
141
|
-
end
|
142
|
-
|
143
|
-
def logout
|
144
|
-
# Not required
|
145
|
-
end
|
146
|
-
|
147
|
-
def list_accounts
|
148
|
-
# Ensure @auth_data contains the necessary credentials
|
149
|
-
raise Error, "Authentication data not found. Please login first." unless @auth_data
|
150
|
-
|
151
|
-
service = "execute-api"
|
152
|
-
region = "ap-southeast-2"
|
153
|
-
http_method = "POST"
|
154
|
-
url = "https://h4ku5ofov2.execute-api.ap-southeast-2.amazonaws.com/prod/secure"
|
155
|
-
email = @auth_data["email"]
|
156
|
-
body = {
|
157
|
-
variables: {},
|
158
|
-
query: <<-GRAPHQL
|
159
|
-
{
|
160
|
-
AccountV2(email: \"george@dewar.co.nz\") {
|
161
|
-
Portfolios {
|
162
|
-
Portfolio
|
163
|
-
PortfolioCode
|
164
|
-
MarketValue
|
165
|
-
IsDefault
|
166
|
-
CanWithdraw
|
167
|
-
InvestorDetails {
|
168
|
-
InvestmentName
|
169
|
-
PrimaryBeneficiarySurname
|
170
|
-
EntityType
|
171
|
-
__typename
|
172
|
-
}
|
173
|
-
__typename
|
174
|
-
}
|
175
|
-
Investment {
|
176
|
-
InvestmentCode
|
177
|
-
InvestmentType
|
178
|
-
Status
|
179
|
-
PriceDate
|
180
|
-
ExternalReference
|
181
|
-
__typename
|
182
|
-
}
|
183
|
-
__typename
|
184
|
-
}
|
185
|
-
}
|
186
|
-
GRAPHQL
|
187
|
-
}.to_json
|
188
|
-
|
189
|
-
signer = Aws::Sigv4::Signer.new(
|
190
|
-
service: service,
|
191
|
-
region: region,
|
192
|
-
access_key_id: @auth_data["access_key_id"],
|
193
|
-
secret_access_key: @auth_data["secret_key"],
|
194
|
-
session_token: @auth_data["session_token"],
|
195
|
-
)
|
196
|
-
|
197
|
-
signature = signer.sign_request(
|
198
|
-
http_method: http_method,
|
199
|
-
url: url,
|
200
|
-
body: body,
|
201
|
-
)
|
202
|
-
|
203
|
-
response = @client.post(url) do |req|
|
204
|
-
req.headers = signature.headers.merge({
|
205
|
-
"Content-Type" => "application/json",
|
206
|
-
})
|
207
|
-
req.body = body
|
208
|
-
end
|
209
|
-
|
210
|
-
# Handle the response
|
211
|
-
if response.success?
|
212
|
-
obj = JSON.parse(response.body)
|
213
|
-
obj["data"]["AccountV2"].map do |account|
|
214
|
-
investment = account["Investment"]
|
215
|
-
portfolio = account["Portfolios"][0]
|
216
|
-
investor_details = portfolio["InvestorDetails"]
|
217
|
-
{
|
218
|
-
accountNo: investment["InvestmentCode"],
|
219
|
-
accountType: "#{investment["InvestmentType"]} - #{portfolio["Portfolio"]}",
|
220
|
-
customerName: investor_details["InvestmentName"],
|
221
|
-
accountBalance: portfolio["MarketValue"],
|
222
|
-
isLiabilityType: false,
|
223
|
-
supportsTransactions: false,
|
224
|
-
dynamicBalance: true,
|
225
|
-
}
|
226
|
-
end
|
227
|
-
else
|
228
|
-
raise Error, "Failed to list accounts: #{response.status}, #{response.body}"
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
private
|
233
|
-
|
234
|
-
def fetch_challenge_id
|
235
|
-
response = @client.post("#{@authsignal_base}/client/challenge") do |req|
|
236
|
-
req.headers = {
|
237
|
-
"Authorization" => "Basic #{Base64.encode64(@authsignal_tenant_id)}",
|
238
|
-
"Content-Type" => "application/json",
|
239
|
-
}
|
240
|
-
req.body = {
|
241
|
-
action: "cognitoAuth",
|
242
|
-
}.to_json
|
243
|
-
end
|
244
|
-
|
245
|
-
if response.success?
|
246
|
-
obj = JSON.parse(response.body)
|
247
|
-
obj["challengeId"]
|
248
|
-
else
|
249
|
-
raise Error, "Failed to get challenge ID: #{response.status}, #{response.body}"
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
def fetch_authentication_options(challenge_id)
|
254
|
-
response = @client.post("#{@authsignal_base}/client/user-authenticators/passkey/authentication-options") do |req|
|
255
|
-
req.headers = {
|
256
|
-
"Authorization" => "Basic #{Base64.encode64(@authsignal_tenant_id)}",
|
257
|
-
"Content-Type" => "application/json",
|
258
|
-
}
|
259
|
-
req.body = {
|
260
|
-
challengeId: challenge_id,
|
261
|
-
}.to_json
|
262
|
-
end
|
263
|
-
|
264
|
-
if response.success?
|
265
|
-
JSON.parse(response.body)
|
266
|
-
else
|
267
|
-
raise Error, "Failed to get authentication options: #{response.status}, #{response.body}"
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
def present_passkey(challenge_id, assertion, device_id)
|
272
|
-
response = @client.post("#{@authsignal_base}/client/verify/passkey") do |req|
|
273
|
-
req.headers = {
|
274
|
-
"Authorization" => "Basic #{Base64.encode64(@authsignal_tenant_id)}",
|
275
|
-
"Content-Type" => "application/json",
|
276
|
-
}
|
277
|
-
req.body = {
|
278
|
-
challengeId: challenge_id,
|
279
|
-
authenticationCredential: {
|
280
|
-
id: assertion.credential_id,
|
281
|
-
rawId: assertion.credential_id,
|
282
|
-
response: {
|
283
|
-
authenticatorData: assertion.authenticator_data,
|
284
|
-
clientDataJSON: assertion.client_data_json,
|
285
|
-
signature: assertion.signature,
|
286
|
-
userHandle: assertion.user_handle,
|
287
|
-
},
|
288
|
-
type: "public-key",
|
289
|
-
clientExtensionResults: {},
|
290
|
-
authenticatorAttachment: "platform",
|
291
|
-
},
|
292
|
-
deviceId: device_id,
|
293
|
-
}.to_json
|
294
|
-
end
|
295
|
-
|
296
|
-
if response.success?
|
297
|
-
JSON.parse(response.body)
|
298
|
-
else
|
299
|
-
raise Error, "Failed to get JWT: #{response.status}, #{response.body}"
|
300
|
-
end
|
301
|
-
end
|
302
|
-
end
|
303
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require "dotenv/load"
|
5
|
+
rescue LoadError
|
6
|
+
# Dotenv is not available, so move on without loading it. It's only used for development.
|
7
|
+
end
|
8
|
+
require "faraday"
|
9
|
+
require "faraday-cookie_jar"
|
10
|
+
require "faraday/follow_redirects"
|
11
|
+
require "aws-sdk-cognitoidentityprovider"
|
12
|
+
require "aws-sdk-cognitoidentity"
|
13
|
+
require "aws-sigv4"
|
14
|
+
require_relative "fido2_client"
|
15
|
+
|
16
|
+
module SimplicityClient
|
17
|
+
Params = Struct.new(:email, :credentialId, :keyAlgorithm, :keyCurve, :keyValue, :userHandle, keyword_init: true)
|
18
|
+
ParamDefinition = Struct.new(:name, :label, :primary_id)
|
19
|
+
|
20
|
+
class Error < StandardError; end
|
21
|
+
|
22
|
+
# For the user interface
|
23
|
+
PARAMS_INFO = [
|
24
|
+
ParamDefinition.new(name: "email", label: "Email address", primary_id: true),
|
25
|
+
ParamDefinition.new(name: "credentialId", label: "Passkey credential ID"),
|
26
|
+
ParamDefinition.new(name: "keyAlgorithm", label: "Passkey key algorithm"),
|
27
|
+
ParamDefinition.new(name: "keyCurve", label: "Passkey key curve"),
|
28
|
+
ParamDefinition.new(name: "keyValue", label: "Passkey value"),
|
29
|
+
ParamDefinition.new(name: "userHandle", label: "Passkey user handle")
|
30
|
+
].freeze
|
31
|
+
|
32
|
+
class Session
|
33
|
+
def initialize
|
34
|
+
@logger = Logger.new $stderr
|
35
|
+
@logger.level = Logger::DEBUG
|
36
|
+
|
37
|
+
@user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"
|
38
|
+
|
39
|
+
@client = Faraday.new(
|
40
|
+
headers: { "User-Agent" => @user_agent },
|
41
|
+
# proxy: "http://Thinkbook.local:8080",
|
42
|
+
# :ssl => {:verify => false}
|
43
|
+
) do |builder|
|
44
|
+
builder.response :follow_redirects
|
45
|
+
builder.response :logger
|
46
|
+
builder.adapter Faraday.default_adapter
|
47
|
+
end
|
48
|
+
|
49
|
+
@authsignal_base = "https://au.api.authsignal.com/v1"
|
50
|
+
@authsignal_tenant_id = "e768822f-b1a1-404c-a685-0e37905ca5f5"
|
51
|
+
end
|
52
|
+
|
53
|
+
# Write the session data out to a string so that the session can be restored later
|
54
|
+
def export
|
55
|
+
{
|
56
|
+
auth_data: @auth_data,
|
57
|
+
}.to_json
|
58
|
+
end
|
59
|
+
|
60
|
+
# Load the session data from a string to restore a session
|
61
|
+
def load(str)
|
62
|
+
json = JSON.parse(str)
|
63
|
+
@auth_data = json["auth_data"]
|
64
|
+
end
|
65
|
+
|
66
|
+
def login(params)
|
67
|
+
client = Aws::CognitoIdentityProvider::Client.new(region: "ap-southeast-2")
|
68
|
+
|
69
|
+
user_pool_id = "ap-southeast-2_abAklW6ap"
|
70
|
+
|
71
|
+
device_id = SecureRandom.uuid
|
72
|
+
passkey = Fido2Client::Passkey.new(params.credentialId, params.keyAlgorithm, params.keyCurve, params.keyValue, params.userHandle)
|
73
|
+
challenge_id = fetch_challenge_id
|
74
|
+
authentication_options = fetch_authentication_options(challenge_id)
|
75
|
+
challenge = authentication_options["options"]["challenge"]
|
76
|
+
assertion = Fido2Client::Client.new.get_assertion(passkey, challenge)
|
77
|
+
passkey_result = present_passkey(challenge_id, assertion, device_id)
|
78
|
+
|
79
|
+
resp = client.initiate_auth(
|
80
|
+
{
|
81
|
+
auth_flow: "CUSTOM_AUTH",
|
82
|
+
auth_parameters: {
|
83
|
+
USERNAME: params.email,
|
84
|
+
},
|
85
|
+
client_metadata: {
|
86
|
+
anonymousId: device_id,
|
87
|
+
},
|
88
|
+
client_id: "kvoiu7unft0c8hqqsa6hkmeu5",
|
89
|
+
},
|
90
|
+
)
|
91
|
+
|
92
|
+
resp = client.respond_to_auth_challenge(
|
93
|
+
{
|
94
|
+
challenge_name: "CUSTOM_CHALLENGE",
|
95
|
+
challenge_responses: {
|
96
|
+
USERNAME: params.email,
|
97
|
+
ANSWER: passkey_result["accessToken"],
|
98
|
+
},
|
99
|
+
client_id: "kvoiu7unft0c8hqqsa6hkmeu5",
|
100
|
+
session: resp.session,
|
101
|
+
},
|
102
|
+
)
|
103
|
+
|
104
|
+
cognito_identity_client = Aws::CognitoIdentity::Client.new(region: "ap-southeast-2")
|
105
|
+
|
106
|
+
# Assuming you have the Identity Pool ID and the ID token
|
107
|
+
identity_pool_id = "ap-southeast-2:0ed33fc6-4cef-4f2e-b634-31c616e108e2"
|
108
|
+
id_token = resp.authentication_result.id_token
|
109
|
+
|
110
|
+
# Get ID from the identity pool
|
111
|
+
id_response = cognito_identity_client.get_id(
|
112
|
+
{
|
113
|
+
identity_pool_id: identity_pool_id,
|
114
|
+
logins: {
|
115
|
+
"cognito-idp.ap-southeast-2.amazonaws.com/#{user_pool_id}" => id_token,
|
116
|
+
},
|
117
|
+
},
|
118
|
+
)
|
119
|
+
|
120
|
+
# Get credentials for the ID
|
121
|
+
credentials_response = cognito_identity_client.get_credentials_for_identity(
|
122
|
+
{
|
123
|
+
identity_id: id_response.identity_id,
|
124
|
+
logins: {
|
125
|
+
"cognito-idp.ap-southeast-2.amazonaws.com/#{user_pool_id}" => id_token,
|
126
|
+
},
|
127
|
+
},
|
128
|
+
)
|
129
|
+
|
130
|
+
access_key_id = credentials_response.credentials.access_key_id
|
131
|
+
secret_key = credentials_response.credentials.secret_key
|
132
|
+
session_token = credentials_response.credentials.session_token
|
133
|
+
|
134
|
+
@auth_data = {
|
135
|
+
email: params.email,
|
136
|
+
access_key_id: access_key_id,
|
137
|
+
secret_key: secret_key,
|
138
|
+
region: "ap-southeast-2",
|
139
|
+
session_token: session_token,
|
140
|
+
}.transform_keys(&:to_s)
|
141
|
+
end
|
142
|
+
|
143
|
+
def logout
|
144
|
+
# Not required
|
145
|
+
end
|
146
|
+
|
147
|
+
def list_accounts
|
148
|
+
# Ensure @auth_data contains the necessary credentials
|
149
|
+
raise Error, "Authentication data not found. Please login first." unless @auth_data
|
150
|
+
|
151
|
+
service = "execute-api"
|
152
|
+
region = "ap-southeast-2"
|
153
|
+
http_method = "POST"
|
154
|
+
url = "https://h4ku5ofov2.execute-api.ap-southeast-2.amazonaws.com/prod/secure"
|
155
|
+
email = @auth_data["email"]
|
156
|
+
body = {
|
157
|
+
variables: {},
|
158
|
+
query: <<-GRAPHQL,
|
159
|
+
{
|
160
|
+
AccountV2(email: \"george@dewar.co.nz\") {
|
161
|
+
Portfolios {
|
162
|
+
Portfolio
|
163
|
+
PortfolioCode
|
164
|
+
MarketValue
|
165
|
+
IsDefault
|
166
|
+
CanWithdraw
|
167
|
+
InvestorDetails {
|
168
|
+
InvestmentName
|
169
|
+
PrimaryBeneficiarySurname
|
170
|
+
EntityType
|
171
|
+
__typename
|
172
|
+
}
|
173
|
+
__typename
|
174
|
+
}
|
175
|
+
Investment {
|
176
|
+
InvestmentCode
|
177
|
+
InvestmentType
|
178
|
+
Status
|
179
|
+
PriceDate
|
180
|
+
ExternalReference
|
181
|
+
__typename
|
182
|
+
}
|
183
|
+
__typename
|
184
|
+
}
|
185
|
+
}
|
186
|
+
GRAPHQL
|
187
|
+
}.to_json
|
188
|
+
|
189
|
+
signer = Aws::Sigv4::Signer.new(
|
190
|
+
service: service,
|
191
|
+
region: region,
|
192
|
+
access_key_id: @auth_data["access_key_id"],
|
193
|
+
secret_access_key: @auth_data["secret_key"],
|
194
|
+
session_token: @auth_data["session_token"],
|
195
|
+
)
|
196
|
+
|
197
|
+
signature = signer.sign_request(
|
198
|
+
http_method: http_method,
|
199
|
+
url: url,
|
200
|
+
body: body,
|
201
|
+
)
|
202
|
+
|
203
|
+
response = @client.post(url) do |req|
|
204
|
+
req.headers = signature.headers.merge({
|
205
|
+
"Content-Type" => "application/json",
|
206
|
+
})
|
207
|
+
req.body = body
|
208
|
+
end
|
209
|
+
|
210
|
+
# Handle the response
|
211
|
+
if response.success?
|
212
|
+
obj = JSON.parse(response.body)
|
213
|
+
obj["data"]["AccountV2"].map do |account|
|
214
|
+
investment = account["Investment"]
|
215
|
+
portfolio = account["Portfolios"][0]
|
216
|
+
investor_details = portfolio["InvestorDetails"]
|
217
|
+
{
|
218
|
+
accountNo: investment["InvestmentCode"],
|
219
|
+
accountType: "#{investment["InvestmentType"]} - #{portfolio["Portfolio"]}",
|
220
|
+
customerName: investor_details["InvestmentName"],
|
221
|
+
accountBalance: portfolio["MarketValue"].to_f,
|
222
|
+
isLiabilityType: false,
|
223
|
+
supportsTransactions: false,
|
224
|
+
dynamicBalance: true,
|
225
|
+
}
|
226
|
+
end
|
227
|
+
else
|
228
|
+
raise Error, "Failed to list accounts: #{response.status}, #{response.body}"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
private
|
233
|
+
|
234
|
+
def fetch_challenge_id
|
235
|
+
response = @client.post("#{@authsignal_base}/client/challenge") do |req|
|
236
|
+
req.headers = {
|
237
|
+
"Authorization" => "Basic #{Base64.encode64(@authsignal_tenant_id)}",
|
238
|
+
"Content-Type" => "application/json",
|
239
|
+
}
|
240
|
+
req.body = {
|
241
|
+
action: "cognitoAuth",
|
242
|
+
}.to_json
|
243
|
+
end
|
244
|
+
|
245
|
+
if response.success?
|
246
|
+
obj = JSON.parse(response.body)
|
247
|
+
obj["challengeId"]
|
248
|
+
else
|
249
|
+
raise Error, "Failed to get challenge ID: #{response.status}, #{response.body}"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def fetch_authentication_options(challenge_id)
|
254
|
+
response = @client.post("#{@authsignal_base}/client/user-authenticators/passkey/authentication-options") do |req|
|
255
|
+
req.headers = {
|
256
|
+
"Authorization" => "Basic #{Base64.encode64(@authsignal_tenant_id)}",
|
257
|
+
"Content-Type" => "application/json",
|
258
|
+
}
|
259
|
+
req.body = {
|
260
|
+
challengeId: challenge_id,
|
261
|
+
}.to_json
|
262
|
+
end
|
263
|
+
|
264
|
+
if response.success?
|
265
|
+
JSON.parse(response.body)
|
266
|
+
else
|
267
|
+
raise Error, "Failed to get authentication options: #{response.status}, #{response.body}"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def present_passkey(challenge_id, assertion, device_id)
|
272
|
+
response = @client.post("#{@authsignal_base}/client/verify/passkey") do |req|
|
273
|
+
req.headers = {
|
274
|
+
"Authorization" => "Basic #{Base64.encode64(@authsignal_tenant_id)}",
|
275
|
+
"Content-Type" => "application/json",
|
276
|
+
}
|
277
|
+
req.body = {
|
278
|
+
challengeId: challenge_id,
|
279
|
+
authenticationCredential: {
|
280
|
+
id: assertion.credential_id,
|
281
|
+
rawId: assertion.credential_id,
|
282
|
+
response: {
|
283
|
+
authenticatorData: assertion.authenticator_data,
|
284
|
+
clientDataJSON: assertion.client_data_json,
|
285
|
+
signature: assertion.signature,
|
286
|
+
userHandle: assertion.user_handle,
|
287
|
+
},
|
288
|
+
type: "public-key",
|
289
|
+
clientExtensionResults: {},
|
290
|
+
authenticatorAttachment: "platform",
|
291
|
+
},
|
292
|
+
deviceId: device_id,
|
293
|
+
}.to_json
|
294
|
+
end
|
295
|
+
|
296
|
+
if response.success?
|
297
|
+
JSON.parse(response.body)
|
298
|
+
else
|
299
|
+
raise Error, "Failed to get JWT: #{response.status}, #{response.body}"
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simplicity_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- George Dewar
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-02-
|
11
|
+
date: 2025-02-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-cognitoidentity
|