mobile_id 0.0.3 → 0.0.4
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/CHANGELOG.md +3 -0
- data/README.md +3 -3
- data/lib/mobile_id.rb +4 -145
- data/lib/mobile_id/auth.rb +150 -0
- data/lib/mobile_id/railtie.rb +2 -2
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ac2213a077be1331007897be4a68ddde6e428cb7b4a0ca712950367d38c5d6c7
|
|
4
|
+
data.tar.gz: 7d7736fb27da9413c707b30ea98797a5dcbe05160ff2fc434b802fdf58310645
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3e04950d1fda22037f6e7f5f72bbf1b0f4038632028940e80bb020626efa54b46d94bd1af4f01c99113db5cf6f58fa6f6e2a6578543ef6b85771e6a88ef86487
|
|
7
|
+
data.tar.gz: bee3d0498d13db91e956c8daf69df2bd4e3c659be5379e40b1a6a9e1d3e7ddbd5f58c310f01c1bd9ed16f6ee92ee4ed6ea027627f770a7915fedda5a3e198382
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -25,8 +25,8 @@ Execute irb:
|
|
|
25
25
|
$ bundle exec irb
|
|
26
26
|
irb(main):001:0> require 'mobile_id'
|
|
27
27
|
=> true
|
|
28
|
-
irb(main):002:0> @mid = MobileId.new(live: false)
|
|
29
|
-
=> #<MobileId:0x000055ac4cb25630 @url="https://tsp.demo.sk.ee/mid-api", @uuid="00000000-0000-0000-0000-000000000000", @name="DEMO", @hash="two+e7UMoFCAXHo8q9AnWqSC58Hhil74RowY8Gg9xQY=">
|
|
28
|
+
irb(main):002:0> @mid = MobileId::Auth.new(live: false)
|
|
29
|
+
=> #<MobileId::Auth:0x000055ac4cb25630 @url="https://tsp.demo.sk.ee/mid-api", @uuid="00000000-0000-0000-0000-000000000000", @name="DEMO", @hash="two+e7UMoFCAXHo8q9AnWqSC58Hhil74RowY8Gg9xQY=">
|
|
30
30
|
irb(main):003:0> auth = @mid.authenticate!(phone: '00000766', personal_code: '60001019906')
|
|
31
31
|
=> {"session_id"=>"34e7eff0-691b-4fad-9798-8db680587b18", "phone"=>"00000766", "phone_calling_code"=>"+372"}
|
|
32
32
|
irb(main):004:0> verify = @mid.verify!(auth)
|
|
@@ -40,7 +40,7 @@ You get verified attributes: personal_code, first_name, last_name, phone, phone_
|
|
|
40
40
|
For live usage, add your relyingPartyUUID (RPUUID) and relyingPartyName what you get from https://www.sk.ee
|
|
41
41
|
|
|
42
42
|
```ruby
|
|
43
|
-
@mid = MobileId.new(live: true, uuid: "39e7eff0-241b-4fad-2798-9db680587b20", name: 'My service name')
|
|
43
|
+
@mid = MobileId::Auth.new(live: true, uuid: "39e7eff0-241b-4fad-2798-9db680587b20", name: 'My service name')
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
Rails with Devise example controller:
|
data/lib/mobile_id.rb
CHANGED
|
@@ -11,151 +11,10 @@ else
|
|
|
11
11
|
I18n.load_path << Dir[File.expand_path("lib/mobile_id/locales") + "/*.yml"]
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
module MobileId
|
|
15
15
|
class Error < StandardError; end
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
LIVE_URL = "https://mid.sk.ee/mid-api"
|
|
19
|
-
TEST_URL = "https://tsp.demo.sk.ee/mid-api"
|
|
20
|
-
|
|
21
|
-
TEST_UUID = "00000000-0000-0000-0000-000000000000"
|
|
22
|
-
TEST_NAME = "DEMO"
|
|
23
|
-
|
|
24
|
-
attr_accessor :url, :uuid, :name, :hash, :cert, :cert_subject
|
|
25
|
-
|
|
26
|
-
def initialize(live:, uuid: nil, name: nil)
|
|
27
|
-
self.url = live == true ? LIVE_URL : TEST_URL
|
|
28
|
-
self.uuid = live == true ? uuid : TEST_UUID
|
|
29
|
-
self.name = live == true ? name : TEST_NAME
|
|
30
|
-
self.hash = Digest::SHA256.base64digest(SecureRandom.uuid)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def authenticate!(phone_calling_code: nil, phone:, personal_code:, language: nil, display_text: nil)
|
|
34
|
-
phone_calling_code ||= '+372'
|
|
35
|
-
full_phone = "#{phone_calling_code}#{phone}"
|
|
36
|
-
language ||=
|
|
37
|
-
case I18n.locale
|
|
38
|
-
when :et
|
|
39
|
-
display_text ||= 'Autentimine'
|
|
40
|
-
'EST'
|
|
41
|
-
when :ru
|
|
42
|
-
display_text ||= 'Аутентификация'
|
|
43
|
-
'RUS'
|
|
44
|
-
else
|
|
45
|
-
display_text ||= 'Authentication'
|
|
46
|
-
'ENG'
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
options = {
|
|
50
|
-
headers: {
|
|
51
|
-
"Content-Type": "application/json"
|
|
52
|
-
},
|
|
53
|
-
query: {},
|
|
54
|
-
body: {
|
|
55
|
-
relyingPartyUUID: uuid,
|
|
56
|
-
relyingPartyName: name,
|
|
57
|
-
phoneNumber: full_phone.to_s.strip,
|
|
58
|
-
nationalIdentityNumber: personal_code.to_s.strip,
|
|
59
|
-
hash: hash,
|
|
60
|
-
hashType: 'SHA256',
|
|
61
|
-
language: language,
|
|
62
|
-
displayText: display_text,
|
|
63
|
-
displayTextFormat: 'GSM-7' # or "UCS-2”
|
|
64
|
-
}.to_json
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
response = HTTParty.post(url + '/authentication', options)
|
|
68
|
-
raise Error, "#{I18n.t('mobile_id.some_error')} #{response}" unless response.code == 200
|
|
69
|
-
|
|
70
|
-
ActiveSupport::HashWithIndifferentAccess.new(
|
|
71
|
-
session_id: response['sessionID'],
|
|
72
|
-
phone: phone,
|
|
73
|
-
phone_calling_code: phone_calling_code
|
|
74
|
-
)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def verify!(auth)
|
|
78
|
-
long_poll!(session_id: auth['session_id'])
|
|
79
|
-
|
|
80
|
-
ActiveSupport::HashWithIndifferentAccess.new(
|
|
81
|
-
personal_code: personal_code,
|
|
82
|
-
first_name: first_name,
|
|
83
|
-
last_name: last_name,
|
|
84
|
-
phone: auth['phone'],
|
|
85
|
-
phone_calling_code: auth['phone_calling_code'],
|
|
86
|
-
auth_provider: 'mobileid' # User::MOBILEID
|
|
87
|
-
)
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def long_poll!(session_id:)
|
|
91
|
-
response = HTTParty.get(url + "/authentication/session/#{session_id}")
|
|
92
|
-
raise Error, "#{I18n.t('mobile_id.some_error')} #{response.code} #{response}" if response.code != 200
|
|
93
|
-
|
|
94
|
-
if response['state'] == 'COMPLETE' && response['result'] != 'OK'
|
|
95
|
-
message =
|
|
96
|
-
case response['result']
|
|
97
|
-
when "TIMEOUT"
|
|
98
|
-
I18n.t('mobile_id.timeout')
|
|
99
|
-
when "NOT_MID_CLIENT"
|
|
100
|
-
I18n.t('mobile_id.user_is_not_mobile_id_client')
|
|
101
|
-
when "USER_CANCELLED"
|
|
102
|
-
I18n.t('mobile_id.user_cancelled')
|
|
103
|
-
when "SIGNATURE_HASH_MISMATCH"
|
|
104
|
-
I18n.t('mobile_id.signature_hash_mismatch')
|
|
105
|
-
when "PHONE_ABSENT"
|
|
106
|
-
I18n.t('mobile_id.phone_absent')
|
|
107
|
-
when "DELIVERY_ERROR"
|
|
108
|
-
I18n.t('mobile_id.delivery_error')
|
|
109
|
-
when "SIM_ERROR"
|
|
110
|
-
I18n.t('mobile_id.sim_error')
|
|
111
|
-
end
|
|
112
|
-
raise Error, message
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
self.cert = OpenSSL::X509::Certificate.new(Base64.decode64(response['cert']))
|
|
116
|
-
self.cert_subject = build_cert_subject
|
|
117
|
-
cert
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
def verification_code
|
|
121
|
-
format("%04d", (Digest::SHA2.new(256).digest(Base64.decode64(hash))[-2..-1].unpack1('n') % 10000))
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def given_name
|
|
125
|
-
cert_subject["GN"].tr(",", " ")
|
|
126
|
-
end
|
|
127
|
-
alias first_name given_name
|
|
128
|
-
|
|
129
|
-
def surname
|
|
130
|
-
cert_subject["SN"].tr(",", " ")
|
|
131
|
-
end
|
|
132
|
-
alias last_name surname
|
|
133
|
-
|
|
134
|
-
def country
|
|
135
|
-
cert_subject["C"].tr(",", " ")
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
def common_name
|
|
139
|
-
cert_subject["CN"]
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def organizational_unit
|
|
143
|
-
cert_subject["OU"]
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
def serial_number
|
|
147
|
-
cert_subject["serialNumber"]
|
|
148
|
-
end
|
|
149
|
-
alias personal_code serial_number
|
|
150
|
-
|
|
151
|
-
private
|
|
152
|
-
|
|
153
|
-
def build_cert_subject
|
|
154
|
-
self.cert_subject = cert.subject.to_utf8.split(/(?<!\\)\,+/).each_with_object({}) do |c, result|
|
|
155
|
-
next unless c.include?("=")
|
|
156
|
-
|
|
157
|
-
key, val = c.split("=")
|
|
158
|
-
result[key] = val
|
|
159
|
-
end
|
|
160
|
-
end
|
|
17
|
+
LOCALES = [:en, :et, :ru]
|
|
161
18
|
end
|
|
19
|
+
|
|
20
|
+
require 'mobile_id/auth'
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MobileId
|
|
4
|
+
class Auth
|
|
5
|
+
# API documentation https://github.com/SK-EID/MID
|
|
6
|
+
LIVE_URL = "https://mid.sk.ee/mid-api"
|
|
7
|
+
TEST_URL = "https://tsp.demo.sk.ee/mid-api"
|
|
8
|
+
|
|
9
|
+
TEST_UUID = "00000000-0000-0000-0000-000000000000"
|
|
10
|
+
TEST_NAME = "DEMO"
|
|
11
|
+
|
|
12
|
+
attr_accessor :url, :uuid, :name, :hash, :cert, :cert_subject
|
|
13
|
+
|
|
14
|
+
def initialize(live:, uuid: nil, name: nil)
|
|
15
|
+
self.url = live == true ? LIVE_URL : TEST_URL
|
|
16
|
+
self.uuid = live == true ? uuid : TEST_UUID
|
|
17
|
+
self.name = live == true ? name : TEST_NAME
|
|
18
|
+
self.hash = Digest::SHA256.base64digest(SecureRandom.uuid)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def authenticate!(phone_calling_code: nil, phone:, personal_code:, language: nil, display_text: nil)
|
|
22
|
+
phone_calling_code ||= '+372'
|
|
23
|
+
full_phone = "#{phone_calling_code}#{phone}"
|
|
24
|
+
language ||=
|
|
25
|
+
case I18n.locale
|
|
26
|
+
when :et
|
|
27
|
+
display_text ||= 'Autentimine'
|
|
28
|
+
'EST'
|
|
29
|
+
when :ru
|
|
30
|
+
display_text ||= 'Аутентификация'
|
|
31
|
+
'RUS'
|
|
32
|
+
else
|
|
33
|
+
display_text ||= 'Authentication'
|
|
34
|
+
'ENG'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
options = {
|
|
38
|
+
headers: {
|
|
39
|
+
"Content-Type": "application/json"
|
|
40
|
+
},
|
|
41
|
+
query: {},
|
|
42
|
+
body: {
|
|
43
|
+
relyingPartyUUID: uuid,
|
|
44
|
+
relyingPartyName: name,
|
|
45
|
+
phoneNumber: full_phone.to_s.strip,
|
|
46
|
+
nationalIdentityNumber: personal_code.to_s.strip,
|
|
47
|
+
hash: hash,
|
|
48
|
+
hashType: 'SHA256',
|
|
49
|
+
language: language,
|
|
50
|
+
displayText: display_text,
|
|
51
|
+
displayTextFormat: 'GSM-7' # or "UCS-2”
|
|
52
|
+
}.to_json
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
response = HTTParty.post(url + '/authentication', options)
|
|
56
|
+
raise Error, "#{I18n.t('mobile_id.some_error')} #{response}" unless response.code == 200
|
|
57
|
+
|
|
58
|
+
ActiveSupport::HashWithIndifferentAccess.new(
|
|
59
|
+
session_id: response['sessionID'],
|
|
60
|
+
phone: phone,
|
|
61
|
+
phone_calling_code: phone_calling_code
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def verify!(auth)
|
|
66
|
+
long_poll!(session_id: auth['session_id'])
|
|
67
|
+
|
|
68
|
+
ActiveSupport::HashWithIndifferentAccess.new(
|
|
69
|
+
personal_code: personal_code,
|
|
70
|
+
first_name: first_name,
|
|
71
|
+
last_name: last_name,
|
|
72
|
+
phone: auth['phone'],
|
|
73
|
+
phone_calling_code: auth['phone_calling_code'],
|
|
74
|
+
auth_provider: 'mobileid' # User::MOBILEID
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def long_poll!(session_id:)
|
|
79
|
+
response = HTTParty.get(url + "/authentication/session/#{session_id}")
|
|
80
|
+
raise Error, "#{I18n.t('mobile_id.some_error')} #{response.code} #{response}" if response.code != 200
|
|
81
|
+
|
|
82
|
+
if response['state'] == 'COMPLETE' && response['result'] != 'OK'
|
|
83
|
+
message =
|
|
84
|
+
case response['result']
|
|
85
|
+
when "TIMEOUT"
|
|
86
|
+
I18n.t('mobile_id.timeout')
|
|
87
|
+
when "NOT_MID_CLIENT"
|
|
88
|
+
I18n.t('mobile_id.user_is_not_mobile_id_client')
|
|
89
|
+
when "USER_CANCELLED"
|
|
90
|
+
I18n.t('mobile_id.user_cancelled')
|
|
91
|
+
when "SIGNATURE_HASH_MISMATCH"
|
|
92
|
+
I18n.t('mobile_id.signature_hash_mismatch')
|
|
93
|
+
when "PHONE_ABSENT"
|
|
94
|
+
I18n.t('mobile_id.phone_absent')
|
|
95
|
+
when "DELIVERY_ERROR"
|
|
96
|
+
I18n.t('mobile_id.delivery_error')
|
|
97
|
+
when "SIM_ERROR"
|
|
98
|
+
I18n.t('mobile_id.sim_error')
|
|
99
|
+
end
|
|
100
|
+
raise Error, message
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
self.cert = OpenSSL::X509::Certificate.new(Base64.decode64(response['cert']))
|
|
104
|
+
self.cert_subject = build_cert_subject
|
|
105
|
+
cert
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def verification_code
|
|
109
|
+
format("%04d", (Digest::SHA2.new(256).digest(Base64.decode64(hash))[-2..-1].unpack1('n') % 10000))
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def given_name
|
|
113
|
+
cert_subject["GN"].tr(",", " ")
|
|
114
|
+
end
|
|
115
|
+
alias first_name given_name
|
|
116
|
+
|
|
117
|
+
def surname
|
|
118
|
+
cert_subject["SN"].tr(",", " ")
|
|
119
|
+
end
|
|
120
|
+
alias last_name surname
|
|
121
|
+
|
|
122
|
+
def country
|
|
123
|
+
cert_subject["C"].tr(",", " ")
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def common_name
|
|
127
|
+
cert_subject["CN"]
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def organizational_unit
|
|
131
|
+
cert_subject["OU"]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def serial_number
|
|
135
|
+
cert_subject["serialNumber"]
|
|
136
|
+
end
|
|
137
|
+
alias personal_code serial_number
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
def build_cert_subject
|
|
142
|
+
self.cert_subject = cert.subject.to_utf8.split(/(?<!\\)\,+/).each_with_object({}) do |c, result|
|
|
143
|
+
next unless c.include?("=")
|
|
144
|
+
|
|
145
|
+
key, val = c.split("=")
|
|
146
|
+
result[key] = val
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
data/lib/mobile_id/railtie.rb
CHANGED
|
@@ -4,8 +4,8 @@ module MobileId
|
|
|
4
4
|
class Railtie < ::Rails::Railtie #:nodoc:
|
|
5
5
|
initializer 'mobile_id' do |app|
|
|
6
6
|
DeviseI18n::Railtie.instance_eval do
|
|
7
|
-
app.config.i18n.available_locales.each do |loc|
|
|
8
|
-
I18n.load_path <<
|
|
7
|
+
(app.config.i18n.available_locales & MobileId::LOCALES).each do |loc|
|
|
8
|
+
I18n.load_path << File.expand_path("locales/#{loc}.yml", __dir__)
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
11
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mobile_id
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Priit Tark
|
|
@@ -104,6 +104,7 @@ files:
|
|
|
104
104
|
- MIT-LICENSE
|
|
105
105
|
- README.md
|
|
106
106
|
- lib/mobile_id.rb
|
|
107
|
+
- lib/mobile_id/auth.rb
|
|
107
108
|
- lib/mobile_id/locales/en.yml
|
|
108
109
|
- lib/mobile_id/locales/et.yml
|
|
109
110
|
- lib/mobile_id/locales/ru.yml
|