yoti 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/CHANGELOG.md +4 -0
- data/CONTRIBUTING.md +15 -11
- data/README.md +103 -89
- data/lib/yoti.rb +9 -1
- data/lib/yoti/client.rb +5 -1
- data/lib/yoti/configuration.rb +1 -1
- data/lib/yoti/errors.rb +3 -0
- data/lib/yoti/http/aml_check_request.rb +25 -0
- data/lib/yoti/http/payloads/aml_address.rb +17 -0
- data/lib/yoti/http/payloads/aml_profile.rb +41 -0
- data/lib/yoti/http/profile_request.rb +24 -0
- data/lib/yoti/http/request.rb +81 -0
- data/lib/yoti/http/signed_request.rb +39 -0
- data/lib/yoti/ssl.rb +2 -2
- data/lib/yoti/version.rb +1 -1
- data/rubocop.yml +6 -5
- data/yoti.gemspec +3 -5
- metadata +10 -5
- data/lib/yoti/request.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db62b520742b6807cac9c0813675327f9dec1bf9
|
4
|
+
data.tar.gz: fb2006107a34a2b9eed606ddc63ed61062ec8d96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af3eebba165eaf25a7ab88a6a07a32de685a2f8ed522a3808519287955100127ef08dbc840856c849c1e25bbf3443a6aad64aa008edc4b29b21230dab5c1ecc2
|
7
|
+
data.tar.gz: ba85c6be0f0978ebcb0209835fd0a4564cf9458b54f88173242705df7a3278b3c4c99bb37b858fb306edc49b998af9c89e809535d98c24cab0eabb8ab6306309
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
6
6
|
|
7
|
+
## 1.2.0 - 2018-02-28
|
8
|
+
### Added
|
9
|
+
- AML (Anti Money Laundering) check service
|
10
|
+
|
7
11
|
## 1.1.0 - 2017-10-24
|
8
12
|
### Changed
|
9
13
|
- `user_profile['selfie']` will return image data instead of an image URI
|
data/CONTRIBUTING.md
CHANGED
@@ -4,11 +4,10 @@ After checking out the repo, run `bundle install` to install dependencies. Then,
|
|
4
4
|
|
5
5
|
You can use [Guard][] to automatically run the tests every time a file in the `lib` or `spec` folder changes.
|
6
6
|
|
7
|
-
|
8
7
|
Run Guard through Bundler with:
|
9
8
|
|
10
9
|
```shell
|
11
|
-
|
10
|
+
bundle exec guard
|
12
11
|
```
|
13
12
|
|
14
13
|
[Guard]: https://github.com/guard/guard
|
@@ -22,12 +21,13 @@ If you wish to compile `.proto` definitions to Ruby, you will need to install [G
|
|
22
21
|
### OSX
|
23
22
|
|
24
23
|
```shell
|
25
|
-
|
24
|
+
brew install protobuf
|
26
25
|
```
|
27
26
|
|
28
27
|
### Ubuntu
|
28
|
+
|
29
29
|
```shell
|
30
|
-
|
30
|
+
sudo apt-get install -y protobuf
|
31
31
|
```
|
32
32
|
|
33
33
|
This gem relies heavily on the [Ruby Protobuf][] gem. For more information on how Google Protobuf works, please see the [Wiki pages][].
|
@@ -35,9 +35,9 @@ This gem relies heavily on the [Ruby Protobuf][] gem. For more information on ho
|
|
35
35
|
Compiling the common and attribute `.proto` definitions can be done with the following commands:
|
36
36
|
|
37
37
|
```shell
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
cd lib/yoti/protobuf/v1
|
39
|
+
protoc -I definitions/attribute-public-api/attrpubapi_v1 --ruby_out ./attribute_public_api definitions/attribute-public-api/attrpubapi_v1/*.proto
|
40
|
+
protoc -I definitions/common-public-api/compubapi_v1/ --ruby_out ./common_public_api definitions/common-public-api/compubapi_v1/*.proto
|
41
41
|
```
|
42
42
|
|
43
43
|
These commands will overwrite the current protobuf Ruby modules, which have been modified. If the protobuf files have to be updated, a good idea would be to change them manually, or generate the files in a new location, and compare the content.
|
@@ -53,30 +53,33 @@ The 100% code coverage requirement must be met before submitting new code.
|
|
53
53
|
This can be checked by opening the generated [SimpleCov][] files:
|
54
54
|
|
55
55
|
```shell
|
56
|
-
|
56
|
+
open coverage/index.html
|
57
57
|
```
|
58
58
|
|
59
59
|
### Style guide
|
60
|
+
|
60
61
|
The Ruby style guide is configured in the [rubocop.yml](rubocop.yml) file and can be checked by running:
|
61
62
|
|
62
63
|
```shell
|
63
|
-
|
64
|
+
bundle exec rake rubocop
|
64
65
|
```
|
65
66
|
|
66
67
|
### Documentation
|
68
|
+
|
67
69
|
The documentation uses the [Yard][] format. Please ensure all new classes and methods are fully documented.
|
68
70
|
|
69
71
|
There are a few Rake tasks to handle documentation:
|
70
72
|
|
71
73
|
```shell
|
72
|
-
|
74
|
+
bundle exec rake measurement
|
73
75
|
```
|
74
76
|
|
75
77
|
Verifies the documentation with [Yardstick][] and generates the `measurement/report.txt` file, containing tips on how to improve the documentation coverage.
|
76
78
|
|
77
79
|
```shell
|
78
|
-
|
80
|
+
bundle exec rake yard
|
79
81
|
```
|
82
|
+
|
80
83
|
Generates [YARD][] documentation in the doc folder.
|
81
84
|
|
82
85
|
### Git
|
@@ -106,6 +109,7 @@ gem push pkg/yoti-[version].gem
|
|
106
109
|
```
|
107
110
|
|
108
111
|
## Submitting a pull request
|
112
|
+
|
109
113
|
1. [Fork the repository.][fork]
|
110
114
|
2. [Create a topic branch.][branch]
|
111
115
|
3. Add specs for your unimplemented feature or bug fix.
|
data/README.md
CHANGED
@@ -4,42 +4,15 @@ Welcome to the Yoti Ruby SDK. This repository contains the tools you need to qui
|
|
4
4
|
|
5
5
|
## Table of Contents
|
6
6
|
|
7
|
-
1
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
4) [Installing the SDK](#installing-the-sdk)-
|
17
|
-
How to install our SDK
|
18
|
-
|
19
|
-
5) [SDK Project import](#sdk-project-import)-
|
20
|
-
How to install the SDK to your project
|
21
|
-
|
22
|
-
6) [Configuration](#configuration)-
|
23
|
-
entry point explanation
|
24
|
-
|
25
|
-
7) [Profile Retrieval](#profile-retrieval)-
|
26
|
-
How to retrieve a Yoti profile using the token
|
27
|
-
|
28
|
-
8) [Handling users](#handling-users)-
|
29
|
-
How to manage users
|
30
|
-
|
31
|
-
9) [API Coverage](#api-coverage)-
|
32
|
-
Attributes defined
|
33
|
-
|
34
|
-
10) [Running the examples](running-the-examples)-
|
35
|
-
Attributes defined
|
36
|
-
|
37
|
-
11) [Support](#support)-
|
38
|
-
Please feel free to reach out
|
39
|
-
|
40
|
-
12) [Change Log](#change-log)
|
41
|
-
|
42
|
-
13) [License](#license)
|
7
|
+
1. [An Architectural view](#an-architectural-view) - High level overview of integration
|
8
|
+
1. [Requirements](#requirements) - Everything you need to get started
|
9
|
+
1. [Installing the SDK](#installing-the-sdk) - How to install our SDK
|
10
|
+
1. [Configuration](#configuration) - Configuring the SDK
|
11
|
+
1. [Profile Retrieval](#profile-retrieval) - How to retrieve a Yoti profile using the token
|
12
|
+
1. [AML Integration](#aml-integration) - How to integrate with Yoti's AML (Anti Money Laundering) service
|
13
|
+
1. [Running the Examples](#running-the-examples) - How to run the example projects provided
|
14
|
+
1. [API Coverage](#api-coverage) - Attributes defined
|
15
|
+
1. [Support](#support) - Please feel free to reach out
|
43
16
|
|
44
17
|
## An Architectural view
|
45
18
|
|
@@ -51,7 +24,6 @@ Yoti SDK carries out for you steps 6, 7, 8 and the profile decryption in step 9.
|
|
51
24
|
|
52
25
|
![alt text](login_flow.png "Login flow")
|
53
26
|
|
54
|
-
|
55
27
|
Yoti also allows you to enable user details verification from your mobile app by means of the Android (TBA) and iOS (TBA) SDKs. In that scenario, your Yoti-enabled mobile app is playing both the role of the browser and the Yoti app. Your back-end doesn't need to handle these cases in a significantly different way, but you might decide to handle the `User-Agent` header in order to provide different responses for desktop and mobile clients.
|
56
28
|
|
57
29
|
## References
|
@@ -92,21 +64,21 @@ gem 'yoti'
|
|
92
64
|
And then execute:
|
93
65
|
|
94
66
|
```shell
|
95
|
-
|
67
|
+
bundle install
|
96
68
|
```
|
97
69
|
|
98
70
|
Or simply run the following command from your terminal:
|
99
71
|
|
100
72
|
```shell
|
101
|
-
|
73
|
+
[sudo] gem install yoti
|
102
74
|
```
|
103
75
|
|
104
|
-
## SDK Project
|
76
|
+
## SDK Project Import
|
105
77
|
|
106
78
|
The gem provides a generator for the initialization file:
|
107
79
|
|
108
80
|
```shell
|
109
|
-
|
81
|
+
rails generate yoti:install
|
110
82
|
```
|
111
83
|
|
112
84
|
The generated initialisation file can be found in `config/initializers/yoti.rb`.
|
@@ -121,6 +93,7 @@ Yoti.configure do |config|
|
|
121
93
|
config.key_file_path = ENV['YOTI_KEY_FILE_PATH']
|
122
94
|
end
|
123
95
|
```
|
96
|
+
|
124
97
|
Make sure the following environment variables can be accessed by your app:
|
125
98
|
|
126
99
|
`YOTI_CLIENT_SDK_ID` - found on the Key settings page on your application dashboard
|
@@ -129,12 +102,12 @@ Make sure the following environment variables can be accessed by your app:
|
|
129
102
|
|
130
103
|
The following options are available:
|
131
104
|
|
132
|
-
Config | Required | Default
|
133
|
-
|
134
|
-
`client_sdk_id` | Yes |
|
135
|
-
`key_file_path` | Yes |
|
136
|
-
`api_url` | No | https://api.yoti.com | Path to Yoti URL used for debugging purposes
|
137
|
-
`api_port` | No | 443
|
105
|
+
Config | Required | Default | Note
|
106
|
+
---------------------|----------|------------------------|-----
|
107
|
+
`client_sdk_id` | Yes | | SDK identifier generated by when you publish your app
|
108
|
+
`key_file_path` | Yes | | Path to the pem file generated when you create your app
|
109
|
+
`api_url` | No | `https://api.yoti.com` | Path to Yoti URL used for debugging purposes
|
110
|
+
`api_port` | No | 443 | Path to Yoti port used for debugging purposes
|
138
111
|
|
139
112
|
Keeping your settings and access keys outside your repository is highly recommended. You can use gems like [dotenv][] to manage environment variables more easily.
|
140
113
|
|
@@ -155,11 +128,7 @@ Yoti.configure do |config|
|
|
155
128
|
end
|
156
129
|
```
|
157
130
|
|
158
|
-
Where `YOTI_KEY` is an environment variable with the following format:
|
159
|
-
|
160
|
-
```
|
161
|
-
YOTI_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEp..."
|
162
|
-
```
|
131
|
+
Where `YOTI_KEY` is an environment variable with the following format: `YOTI_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEp..."`
|
163
132
|
|
164
133
|
An easier way of setting this on Heroku would be to use the [Heroku Command Line][]
|
165
134
|
|
@@ -169,9 +138,7 @@ heroku config:add YOTI_KEY ="$(cat your-access-security.pem)"
|
|
169
138
|
|
170
139
|
[Heroku Command Line]: https://devcenter.heroku.com/articles/heroku-command-line
|
171
140
|
|
172
|
-
|
173
|
-
|
174
|
-
## Profile retrieval
|
141
|
+
## Profile Retrieval
|
175
142
|
|
176
143
|
When your application receives a token via the exposed endpoint (it will be assigned to a query string parameter named `token`), you can easily retrieve the user profile:
|
177
144
|
|
@@ -189,11 +156,14 @@ else
|
|
189
156
|
end
|
190
157
|
```
|
191
158
|
|
192
|
-
The `user_profile
|
159
|
+
The `user_profile` object provides a set of attributes corresponding to user attributes. Whether the attributes are present or not depends on the settings you have applied to your app on Yoti Dashboard.
|
193
160
|
|
194
|
-
### Handling
|
161
|
+
### Handling Users
|
195
162
|
|
196
|
-
When you retrieve the user profile, you receive a user ID generated by Yoti exclusively for your application.
|
163
|
+
When you retrieve the user profile, you receive a user ID generated by Yoti exclusively for your application.
|
164
|
+
This means that if the same individual logs into another app, Yoti will assign her/him a different ID.
|
165
|
+
You can use this ID to verify whether (for your application) the retrieved profile identifies a new or an existing user.
|
166
|
+
Here is an example of how this works:
|
197
167
|
|
198
168
|
```ruby
|
199
169
|
if yoti_activity_details.outcome == 'SUCCESS'
|
@@ -209,9 +179,54 @@ else
|
|
209
179
|
end
|
210
180
|
```
|
211
181
|
|
212
|
-
Where `your_user_search_function` is a piece of logic in your app that is supposed to find a user, given a user_id. Regardless of
|
182
|
+
Where `your_user_search_function` is a piece of logic in your app that is supposed to find a user, given a user_id. Regardless of whether the user is a new or an existing one, Yoti will always provide their profile, so you don't necessarily need to store it.
|
183
|
+
|
184
|
+
## AML Integration
|
185
|
+
|
186
|
+
Yoti provides an AML (Anti Money Laundering) check service to allow a deeper KYC process to prevent fraud. This is a chargeable service, so please contact [sdksupport@yoti.com](mailto:sdksupport@yoti.com) for more information.
|
187
|
+
|
188
|
+
Yoti will provide a boolean result on the following checks:
|
189
|
+
|
190
|
+
* PEP list - Verify against Politically Exposed Persons list
|
191
|
+
* Fraud list - Verify against US Social Security Administration Fraud (SSN Fraud) list
|
192
|
+
* Watch list - Verify against watch lists from the Office of Foreign Assets Control
|
193
|
+
|
194
|
+
To use this functionality you must ensure your application is assigned to your Organisation in the Yoti Dashboard - please see here for further information.
|
213
195
|
|
214
|
-
|
196
|
+
For the AML check you will need to provide the following:
|
197
|
+
|
198
|
+
* Data provided by Yoti (please ensure you have selected the Given name(s) and Family name attributes from the Data tab in the Yoti Dashboard)
|
199
|
+
* Given name(s)
|
200
|
+
* Family name
|
201
|
+
* Data that must be collected from the user:
|
202
|
+
* Country of residence (must be an ISO 3166 3-letter code)
|
203
|
+
* Social Security Number (US citizens only)
|
204
|
+
* Postcode/Zip code (US citizens only)
|
205
|
+
|
206
|
+
### Consent
|
207
|
+
|
208
|
+
Performing an AML check on a person *requires* their consent.
|
209
|
+
**You must ensure you have user consent *before* using this service.**
|
210
|
+
|
211
|
+
### Code Example
|
212
|
+
|
213
|
+
Given a YotiClient initialised with your SDK ID and KeyPair (see [Client Initialisation](#client-initialisation)) performing an AML check is a straightforward case of providing basic profile data.
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
require 'yoti'
|
217
|
+
|
218
|
+
Yoti.configure do |config|
|
219
|
+
config.client_sdk_id = ENV['YOTI_CLIENT_SDK_ID']
|
220
|
+
config.key_file_path = ENV['YOTI_KEY_FILE_PATH']
|
221
|
+
end
|
222
|
+
|
223
|
+
aml_address = Yoti::AmlAddress.new('GBR')
|
224
|
+
aml_profile = Yoti::AmlProfile.new('Edward Richard George', 'Heath', aml_address)
|
225
|
+
|
226
|
+
puts Yoti::Client.aml_check(aml_profile)
|
227
|
+
```
|
228
|
+
|
229
|
+
## Running the Examples
|
215
230
|
|
216
231
|
The examples can be found in the [examples folder](examples).
|
217
232
|
For them to work you will need a working callback URL that your browser can redirect to. The callback URL for both examples will be: `http://your-local-url.domain/profile`.
|
@@ -230,43 +245,42 @@ Visiting the `http://your-local-url.domain` should show a Yoti Connect button
|
|
230
245
|
|
231
246
|
* rename the [.env.default](examples/sinatra/.env.default) file to `.env` and fill in the required configuration values
|
232
247
|
* install the dependencies with `bundle install`
|
233
|
-
* start the server `
|
248
|
+
* start the server `ruby ./app.rb`
|
234
249
|
|
235
250
|
Visiting the `http://your-local-url.domain` should show a Yoti Connect button
|
236
251
|
|
237
|
-
|
252
|
+
### AML Check
|
253
|
+
|
254
|
+
* rename the [.env.default](examples/aml_check/.env.default) file to `.env` and fill in the required configuration values
|
255
|
+
* install the dependencies with `bundle install`
|
256
|
+
* run the script with `ruby ./app.rb`
|
257
|
+
|
258
|
+
## API Coverage
|
238
259
|
|
239
260
|
* Activity Details
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
* [X]
|
261
|
+
* [X] User ID `user_id`
|
262
|
+
* [X] Profile
|
263
|
+
* [X] Selfie `selfie`
|
264
|
+
* [X] Full Name `full_name`
|
265
|
+
* [X] Given Names `given_names`
|
266
|
+
* [X] Family Name `family_name`
|
267
|
+
* [X] Mobile Number `phone_number`
|
268
|
+
* [X] Email Address `email_address`
|
269
|
+
* [X] Age / Date of Birth `date_of_birth`
|
270
|
+
* [X] Age / Verify Condition `age_[over|under]:[1-999]`
|
271
|
+
* [X] Address `postal_address`
|
272
|
+
* [X] Gender `gender`
|
273
|
+
* [X] Nationality `nationality`
|
274
|
+
* [X] Base64 Selfie URI `base64_selfie_uri`
|
252
275
|
|
253
276
|
## Support
|
254
277
|
|
255
278
|
For any questions or support please email [sdksupport@yoti.com](mailto:sdksupport@yoti.com).
|
256
|
-
Please provide the following
|
257
|
-
|
258
|
-
- Computer Type
|
259
|
-
- OS Version
|
260
|
-
- Version of Ruby being used
|
261
|
-
- Screenshot
|
262
|
-
|
263
|
-
|
264
|
-
## Changelog
|
265
|
-
|
266
|
-
See recent changes in the [release notes][release notes] or the [changelog](CHANGELOG.md).
|
267
|
-
|
268
|
-
[release notes]: https://github.com/getyoti/yoti-ruby-sdk/releases
|
279
|
+
Please provide the following to get you up and working as quickly as possible:
|
269
280
|
|
270
|
-
|
281
|
+
* Computer type
|
282
|
+
* OS version
|
283
|
+
* Version of Ruby being used
|
284
|
+
* Screenshot
|
271
285
|
|
272
|
-
|
286
|
+
Once we have answered your question we may contact you again to discuss Yoti products and services. If you’d prefer us not to do this, please let us know when you e-mail.
|
data/lib/yoti.rb
CHANGED
@@ -2,7 +2,15 @@ require_relative 'yoti/version'
|
|
2
2
|
require_relative 'yoti/configuration'
|
3
3
|
require_relative 'yoti/errors'
|
4
4
|
require_relative 'yoti/ssl'
|
5
|
-
|
5
|
+
|
6
|
+
require_relative 'yoti/http/payloads/aml_address'
|
7
|
+
require_relative 'yoti/http/payloads/aml_profile'
|
8
|
+
require_relative 'yoti/http/aml_check_request'
|
9
|
+
|
10
|
+
require_relative 'yoti/http/signed_request'
|
11
|
+
require_relative 'yoti/http/profile_request'
|
12
|
+
require_relative 'yoti/http/request'
|
13
|
+
|
6
14
|
require_relative 'yoti/activity_details'
|
7
15
|
require_relative 'yoti/client'
|
8
16
|
require_relative 'yoti/protobuf/v1/protobuf'
|
data/lib/yoti/client.rb
CHANGED
@@ -6,7 +6,7 @@ module Yoti
|
|
6
6
|
# @param encrypted_connect_token [String] token provided as a base 64 string
|
7
7
|
# @return [Object] an ActivityDetails instance encapsulating the user profile
|
8
8
|
def self.get_activity_details(encrypted_connect_token)
|
9
|
-
receipt = Yoti::
|
9
|
+
receipt = Yoti::ProfileRequest.new(encrypted_connect_token).receipt
|
10
10
|
encrypted_data = Protobuf.current_user(receipt)
|
11
11
|
|
12
12
|
return ActivityDetails.new(receipt) if encrypted_data.nil?
|
@@ -16,5 +16,9 @@ module Yoti
|
|
16
16
|
decrypted_profile = Protobuf.attribute_list(decrypted_data)
|
17
17
|
ActivityDetails.new(receipt, decrypted_profile)
|
18
18
|
end
|
19
|
+
|
20
|
+
def self.aml_check(aml_profile)
|
21
|
+
Yoti::AmlCheckRequest.new(aml_profile).response
|
22
|
+
end
|
19
23
|
end
|
20
24
|
end
|
data/lib/yoti/configuration.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Yoti
|
2
2
|
class Configuration
|
3
3
|
attr_accessor :client_sdk_id, :key_file_path, :key, :sdk_identifier,
|
4
|
-
:api_url, :api_port, :api_version
|
4
|
+
:api_url, :api_port, :api_version
|
5
5
|
|
6
6
|
# Set config variables by using a configuration block
|
7
7
|
def initialize
|
data/lib/yoti/errors.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Yoti
|
2
|
+
# Manage the API's AML check requests
|
3
|
+
class AmlCheckRequest
|
4
|
+
def initialize(aml_profile)
|
5
|
+
@aml_profile = aml_profile
|
6
|
+
@payload = aml_profile.payload
|
7
|
+
@request = request
|
8
|
+
end
|
9
|
+
|
10
|
+
# @return [String] a JSON representation of the AML check response
|
11
|
+
def response
|
12
|
+
JSON.parse(@request.body)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def request
|
18
|
+
yoti_request = Yoti::Request.new
|
19
|
+
yoti_request.http_method = 'POST'
|
20
|
+
yoti_request.endpoint = 'aml-check'
|
21
|
+
yoti_request.payload = @payload
|
22
|
+
yoti_request
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Yoti
|
2
|
+
# Manages the AML check Address object
|
3
|
+
class AmlAddress
|
4
|
+
# @return [String] the country of residence in a ISO 3166 3-letter code format
|
5
|
+
attr_accessor :country
|
6
|
+
|
7
|
+
# @return [String] the postcode required for USA, optional otherwise
|
8
|
+
attr_accessor :post_code
|
9
|
+
|
10
|
+
def initialize(country, post_code = nil)
|
11
|
+
raise AmlError, 'AmlAddress requires a country.' if country.to_s.empty?
|
12
|
+
|
13
|
+
@country = country
|
14
|
+
@post_code = post_code
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Yoti
|
2
|
+
# Manages the AML check Profile object
|
3
|
+
class AmlProfile
|
4
|
+
def initialize(given_names, family_name, aml_address, ssn = nil)
|
5
|
+
@given_names = given_names
|
6
|
+
@family_name = family_name
|
7
|
+
@ssn = ssn
|
8
|
+
@address = aml_address
|
9
|
+
|
10
|
+
raise AmlError, 'The AML request requires given names, family name and an ISO 3166 3-letter code.' if profile_invalid
|
11
|
+
raise AmlError, 'Request for USA require a valid SSN and postcode.' if usa_invalid
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Object] the AML check request body
|
15
|
+
def payload
|
16
|
+
{
|
17
|
+
given_names: @given_names,
|
18
|
+
family_name: @family_name,
|
19
|
+
ssn: @ssn,
|
20
|
+
address: {
|
21
|
+
country: @address.country,
|
22
|
+
post_code: @address.post_code
|
23
|
+
}
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def profile_invalid
|
30
|
+
@given_names.to_s.empty? || @family_name.to_s.empty? || address_invalid
|
31
|
+
end
|
32
|
+
|
33
|
+
def address_invalid
|
34
|
+
!@address.is_a?(AmlAddress) || @address.country.to_s.length != 3
|
35
|
+
end
|
36
|
+
|
37
|
+
def usa_invalid
|
38
|
+
@address.country == 'USA' && (@ssn.to_s.empty? || @address.post_code.to_s.empty?)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Yoti
|
2
|
+
# Manage the API's profile requests
|
3
|
+
class ProfileRequest
|
4
|
+
def initialize(encrypted_connect_token)
|
5
|
+
@encrypted_connect_token = encrypted_connect_token
|
6
|
+
@request = request
|
7
|
+
end
|
8
|
+
|
9
|
+
# @return [String] a JSON representation of the profile response receipt
|
10
|
+
def receipt
|
11
|
+
JSON.parse(@request.body)['receipt']
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def request
|
17
|
+
yoti_request = Yoti::Request.new
|
18
|
+
yoti_request.encrypted_connect_token = @encrypted_connect_token
|
19
|
+
yoti_request.http_method = 'GET'
|
20
|
+
yoti_request.endpoint = 'profile'
|
21
|
+
yoti_request
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Yoti
|
2
|
+
# Manage the API's HTTPS requests
|
3
|
+
class Request
|
4
|
+
# @return [String] the URL token received from Yoti Connect
|
5
|
+
attr_accessor :encrypted_connect_token
|
6
|
+
|
7
|
+
# @return [String] the HTTP method used for the request
|
8
|
+
# The allowed methods are: GET, DELETE, POST, PUT, PATCH
|
9
|
+
attr_accessor :http_method
|
10
|
+
|
11
|
+
# @return [String] the API endpoint for the request
|
12
|
+
attr_accessor :endpoint
|
13
|
+
|
14
|
+
# @return [Hash] the body sent with the request
|
15
|
+
attr_accessor :payload
|
16
|
+
|
17
|
+
# Makes a HTTP request after signing the headers
|
18
|
+
# @return [Hash] the body from the HTTP request
|
19
|
+
def body
|
20
|
+
raise RequestError, 'The request requires a HTTP method.' unless @http_method
|
21
|
+
raise RequestError, 'The payload needs to be a hash.' unless @payload.to_s.empty? || @payload.is_a?(Hash)
|
22
|
+
|
23
|
+
res = Net::HTTP.start(uri.hostname, Yoti.configuration.api_port, use_ssl: https_uri?) do |http|
|
24
|
+
signed_request = SignedRequest.new(unsigned_request, path, @payload).sign
|
25
|
+
http.request(signed_request)
|
26
|
+
end
|
27
|
+
|
28
|
+
raise RequestError, "Unsuccessful Yoti API call: #{res.message}" unless res.code == '200'
|
29
|
+
res.body
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def unsigned_request
|
35
|
+
case @http_method
|
36
|
+
when 'GET'
|
37
|
+
http_req = Net::HTTP::Get.new(uri)
|
38
|
+
when 'DELETE'
|
39
|
+
http_req = Net::HTTP::Delete.new(uri)
|
40
|
+
when 'POST'
|
41
|
+
http_req = Net::HTTP::Post.new(uri)
|
42
|
+
http_req.body = @payload.to_json unless @payload.to_s.empty?
|
43
|
+
when 'PUT'
|
44
|
+
http_req = Net::HTTP::Put.new(uri)
|
45
|
+
http_req.body = @payload.to_json unless @payload.to_s.empty?
|
46
|
+
when 'PATCH'
|
47
|
+
http_req = Net::HTTP::Patch.new(uri)
|
48
|
+
http_req.body = @payload.to_json unless @payload.to_s.empty?
|
49
|
+
else
|
50
|
+
raise RequestError, "Request method not allowed: #{@http_method}"
|
51
|
+
end
|
52
|
+
|
53
|
+
http_req
|
54
|
+
end
|
55
|
+
|
56
|
+
def uri
|
57
|
+
@uri ||= URI(Yoti.configuration.api_endpoint + path)
|
58
|
+
end
|
59
|
+
|
60
|
+
def path
|
61
|
+
@path ||= begin
|
62
|
+
nonce = SecureRandom.uuid
|
63
|
+
timestamp = Time.now.to_i
|
64
|
+
|
65
|
+
"/#{@endpoint}/#{token}"\
|
66
|
+
"?nonce=#{nonce}"\
|
67
|
+
"×tamp=#{timestamp}"\
|
68
|
+
"&appId=#{Yoti.configuration.client_sdk_id}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def token
|
73
|
+
return '' unless @encrypted_connect_token
|
74
|
+
Yoti::SSL.decrypt_token(@encrypted_connect_token)
|
75
|
+
end
|
76
|
+
|
77
|
+
def https_uri?
|
78
|
+
uri.scheme == 'https'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Yoti
|
4
|
+
# Converts a basic Net::HTTP request into a Yoti Signed Request
|
5
|
+
class SignedRequest
|
6
|
+
def initialize(unsigned_request, path, payload = {})
|
7
|
+
@http_req = unsigned_request
|
8
|
+
@path = path
|
9
|
+
@payload = payload
|
10
|
+
@auth_key = Yoti::SSL.auth_key_from_pem
|
11
|
+
end
|
12
|
+
|
13
|
+
def sign
|
14
|
+
@http_req['X-Yoti-Auth-Key'] = @auth_key
|
15
|
+
@http_req['X-Yoti-Auth-Digest'] = message_signature
|
16
|
+
@http_req['X-Yoti-SDK'] = Yoti.configuration.sdk_identifier
|
17
|
+
@http_req['Content-Type'] = 'application/json'
|
18
|
+
@http_req['Accept'] = 'application/json'
|
19
|
+
@http_req
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def message_signature
|
25
|
+
@message_signature ||= Yoti::SSL.get_secure_signature("#{http_method}&#{@path}#{payload_string}")
|
26
|
+
end
|
27
|
+
|
28
|
+
def http_method
|
29
|
+
@http_req.method
|
30
|
+
end
|
31
|
+
|
32
|
+
# Create the base64 encoded request body
|
33
|
+
def payload_string
|
34
|
+
return '' unless @payload
|
35
|
+
|
36
|
+
'&' + Base64.strict_encode64(@payload.to_json)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/yoti/ssl.rb
CHANGED
@@ -26,7 +26,7 @@ module Yoti
|
|
26
26
|
|
27
27
|
begin
|
28
28
|
private_key.private_decrypt(Base64.urlsafe_decode64(encrypted_connect_token))
|
29
|
-
rescue => error
|
29
|
+
rescue StandardError => error
|
30
30
|
raise SslError, "Could not decrypt token. #{error}"
|
31
31
|
end
|
32
32
|
end
|
@@ -63,7 +63,7 @@ module Yoti
|
|
63
63
|
|
64
64
|
def private_key
|
65
65
|
@private_key ||= OpenSSL::PKey::RSA.new(pem)
|
66
|
-
rescue => error
|
66
|
+
rescue StandardError => error
|
67
67
|
raise SslError, "The secure key is invalid. #{error}"
|
68
68
|
end
|
69
69
|
end
|
data/lib/yoti/version.rb
CHANGED
data/rubocop.yml
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
AllCops:
|
2
2
|
DisplayCopNames: true
|
3
3
|
DisplayStyleGuide: true
|
4
|
-
TargetRubyVersion: 2.
|
4
|
+
TargetRubyVersion: 2.1
|
5
5
|
Exclude:
|
6
6
|
- 'examples/rails/config/**/*'
|
7
7
|
|
8
8
|
Metrics/AbcSize:
|
9
|
-
Max:
|
9
|
+
Max: 23
|
10
10
|
Exclude:
|
11
11
|
- examples/rails/app/controllers/yoti_controller.rb
|
12
12
|
|
@@ -14,14 +14,15 @@ Metrics/BlockLength:
|
|
14
14
|
Exclude:
|
15
15
|
- spec/**/**/*.rb
|
16
16
|
|
17
|
+
Metrics/CyclomaticComplexity:
|
18
|
+
Max: 9
|
19
|
+
|
17
20
|
Metrics/LineLength:
|
18
21
|
Enabled: false
|
19
22
|
|
20
23
|
Metrics/MethodLength:
|
21
24
|
CountComments: false
|
22
|
-
Max:
|
23
|
-
Exclude:
|
24
|
-
- examples/rails/app/controllers/yoti_controller.rb
|
25
|
+
Max: 19
|
25
26
|
|
26
27
|
Style/Documentation:
|
27
28
|
Enabled: false
|
data/yoti.gemspec
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
1
|
lib = File.expand_path('../lib', __FILE__)
|
4
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
3
|
require 'yoti/version'
|
@@ -11,11 +9,11 @@ Gem::Specification.new do |spec|
|
|
11
9
|
spec.email = ['tech@yoti.com']
|
12
10
|
|
13
11
|
spec.summary = 'Yoti Ruby SDK for back-end integration.'
|
14
|
-
spec.description = <<-
|
12
|
+
spec.description = <<-DESC
|
15
13
|
This gem contains the tools you need to quickly integrate your Ruby back-end
|
16
14
|
with Yoti, so that your users can share their identity details with your
|
17
15
|
application in a secure and trusted way.
|
18
|
-
|
16
|
+
DESC
|
19
17
|
|
20
18
|
spec.homepage = 'https://github.com/getyoti/yoti-ruby-sdk'
|
21
19
|
spec.license = 'MIT'
|
@@ -33,7 +31,7 @@ Gem::Specification.new do |spec|
|
|
33
31
|
spec.add_development_dependency 'rake', '~> 12.0'
|
34
32
|
spec.add_development_dependency 'rspec', '~> 3.5'
|
35
33
|
spec.add_development_dependency 'simplecov', '~> 0.13'
|
36
|
-
spec.add_development_dependency 'webmock', '~>
|
34
|
+
spec.add_development_dependency 'webmock', '~> 3.3'
|
37
35
|
spec.add_development_dependency 'yard', '~> 0.9'
|
38
36
|
spec.add_development_dependency 'yardstick', '~> 0.9'
|
39
37
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yoti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sebastian Zaremba
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-02-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: protobuf
|
@@ -114,14 +114,14 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
117
|
+
version: '3.3'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
124
|
+
version: '3.3'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: yard
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -175,6 +175,12 @@ files:
|
|
175
175
|
- lib/yoti/client.rb
|
176
176
|
- lib/yoti/configuration.rb
|
177
177
|
- lib/yoti/errors.rb
|
178
|
+
- lib/yoti/http/aml_check_request.rb
|
179
|
+
- lib/yoti/http/payloads/aml_address.rb
|
180
|
+
- lib/yoti/http/payloads/aml_profile.rb
|
181
|
+
- lib/yoti/http/profile_request.rb
|
182
|
+
- lib/yoti/http/request.rb
|
183
|
+
- lib/yoti/http/signed_request.rb
|
178
184
|
- lib/yoti/protobuf/v1/attribute_public_api/attribute.pb.rb
|
179
185
|
- lib/yoti/protobuf/v1/attribute_public_api/list.pb.rb
|
180
186
|
- lib/yoti/protobuf/v1/attribute_public_api/signing.pb.rb
|
@@ -184,7 +190,6 @@ files:
|
|
184
190
|
- lib/yoti/protobuf/v1/definitions/attribute-public-api/attrpubapi_v1/signing.proto
|
185
191
|
- lib/yoti/protobuf/v1/definitions/common-public-api/compubapi_v1/encrypted_data.proto
|
186
192
|
- lib/yoti/protobuf/v1/protobuf.rb
|
187
|
-
- lib/yoti/request.rb
|
188
193
|
- lib/yoti/ssl.rb
|
189
194
|
- lib/yoti/version.rb
|
190
195
|
- login_flow.png
|
data/lib/yoti/request.rb
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
module Yoti
|
2
|
-
# Manage the API's HTTPS requests
|
3
|
-
class Request
|
4
|
-
def initialize(encrypted_connect_token)
|
5
|
-
@encrypted_connect_token = encrypted_connect_token
|
6
|
-
@auth_key = Yoti::SSL.auth_key_from_pem
|
7
|
-
end
|
8
|
-
|
9
|
-
# @return [Hash] the receipt key from the request hash response
|
10
|
-
def receipt
|
11
|
-
request['receipt']
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def request
|
17
|
-
res = Net::HTTP.start(uri.hostname, Yoti.configuration.api_port, use_ssl: https_uri?) do |http|
|
18
|
-
http.request(req)
|
19
|
-
end
|
20
|
-
|
21
|
-
raise RequestError, "Unsuccessful Yoti API call: #{res.message}" unless res.code == '200'
|
22
|
-
JSON.parse(res.body)
|
23
|
-
end
|
24
|
-
|
25
|
-
def req
|
26
|
-
http_req = Net::HTTP::Get.new(uri)
|
27
|
-
http_req['X-Yoti-Auth-Key'] = @auth_key
|
28
|
-
http_req['X-Yoti-Auth-Digest'] = message_signature
|
29
|
-
http_req['X-Yoti-SDK'] = @sdk_identifier
|
30
|
-
http_req['Content-Type'] = 'application/json'
|
31
|
-
http_req['Accept'] = 'application/json'
|
32
|
-
http_req
|
33
|
-
end
|
34
|
-
|
35
|
-
def message_signature
|
36
|
-
@message_signature ||= Yoti::SSL.get_secure_signature("GET&#{path}")
|
37
|
-
end
|
38
|
-
|
39
|
-
def uri
|
40
|
-
@uri ||= URI(Yoti.configuration.api_endpoint + path)
|
41
|
-
end
|
42
|
-
|
43
|
-
def path
|
44
|
-
@path ||= begin
|
45
|
-
nonce = SecureRandom.uuid
|
46
|
-
timestamp = Time.now.to_i
|
47
|
-
"/profile/#{token}?nonce=#{nonce}×tamp=#{timestamp}&appId=#{Yoti.configuration.client_sdk_id}"
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def token
|
52
|
-
@token ||= Yoti::SSL.decrypt_token(@encrypted_connect_token)
|
53
|
-
end
|
54
|
-
|
55
|
-
def https_uri?
|
56
|
-
uri.scheme == 'https'
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|