myinfo_ruby 1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fae89a585898afb8d15885bc4b5522663a4c486c
4
+ data.tar.gz: 791fcb3e5ca1fcce6c5a1fb00eda437e0de7eec4
5
+ SHA512:
6
+ metadata.gz: e12a0c3c2923f33f9645db8f562d16023ff14a5cfc46ca2ff06d77cf20cd77bc98e5254581ab79988683b756cc6b86976dc1fa3c82315a80996181e340701b4b
7
+ data.tar.gz: 74469086e899a695d14dc1f99db1a4b630d7b445dd9edcc18cffdbe69861b5438ed5dd8824fe9338ef678f9e05b9c0c73d50279ba827bf6d00f04c877f255c23
@@ -0,0 +1,10 @@
1
+ require "myinfo_ruby/version"
2
+ require "myinfo_ruby/client"
3
+
4
+ module MyinfoRuby
5
+ class << self
6
+ def new config
7
+ @client ||= MyinfoRuby::Client.new config
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,42 @@
1
+ require_relative 'security'
2
+
3
+ module MyinfoRuby
4
+ class Client
5
+ include MyinfoRuby::Security
6
+
7
+ def initialize config
8
+ # Token api url
9
+ @token_url = config[:token_url]
10
+ # Personal api url
11
+ @personal_url = config[:personal_url]
12
+ # authorization code
13
+ @code = config[:code]
14
+ @private_key = config[:private_key]
15
+ @client_id = config[:client_id]
16
+ @client_secret = config[:client_secret]
17
+ # Attributes to be fetched from MyInfo
18
+ @attributes = config[:attributes]
19
+ @redirect_uri = config[:redirect_url]
20
+ @auth_level = config[:auth_level]
21
+ end
22
+
23
+ def fetch_personal
24
+ # ********* Access Token *********
25
+ token_response = create_token_request(@token_url, @code, @redirect_url, @client_id, @client_secret, @auth_level, @private_key)
26
+
27
+ # ********* Verify JWS *********
28
+ decoded_jws = verify_JWS(token_response, @private_key)
29
+
30
+ # ********* Personal Data *********'
31
+ personal_response = get_personal_data(@personal_url, decoded_jws['sub'], token_response, @client_id, @attributes, @auth_level, @private_key)
32
+ if personal_response.code == 200
33
+ personal_data = decrypt_JWE_response(personal_response, @private_key)
34
+ personal_data['uinfin'] = decoded_jws['sub']
35
+ personal_data
36
+ else
37
+ puts "#{personal_response.code} : Error occoured while Fetching data"
38
+ personal_response
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,99 @@
1
+ require 'openssl'
2
+ require 'rest-client'
3
+ require 'jose'
4
+ module MyinfoRuby
5
+ module Security
6
+ def create_token_request(token_url, code, redirect_url, client_id, client_secret, auth_level, private_key)
7
+ token_params = {
8
+ grant_type: 'authorization_code',
9
+ code: code,
10
+ redirect_uri: redirect_url,
11
+ client_id: client_id,
12
+ client_secret: client_secret
13
+ }
14
+ token_header = {'Content-Type' => "application/x-www-form-urlencoded", 'Cache-Control' => "no-cache"}
15
+
16
+ authorization_header = nil
17
+ if auth_level == 'L2'
18
+ authorization_header = generate_signature(token_url, token_params, 'POST', 'application/x-www-form-urlencoded', client_id, private_key)
19
+
20
+ token_header.merge!({"Authorization" => authorization_header})
21
+ end
22
+ token_response = RestClient.post(token_url, token_params, token_header)
23
+ JSON.parse(token_response)
24
+ end
25
+
26
+ def get_personal_data(personal_url, uinfin, token_response, client_id, attributes, auth_level, private_key)
27
+ puts '------ Fetching personal data ------'
28
+ parameters = {
29
+ :client_id => client_id,
30
+ :attributes => attributes
31
+ }
32
+ authorization_header = token_response['token_type']+' '+token_response['access_token']
33
+ url = personal_url + "/" + uinfin + "/"
34
+
35
+ if auth_level == 'L2'
36
+ auth_header = generate_signature(url, parameters, 'GET', 'application/x-www-form-urlencoded', client_id, private_key)
37
+
38
+ authorization_header = auth_header+','+authorization_header
39
+ end
40
+
41
+ personal_header = {
42
+ 'Cache-Control' => "no-cache",
43
+ :Authorization => authorization_header,
44
+ :params => parameters
45
+ }
46
+ RestClient.get(url, personal_header)
47
+ end
48
+
49
+ # Verify JWS
50
+ def verify_JWS(token_response, private_key)
51
+ jwk = JOSE::JWK.from_pem_file(private_key)
52
+ decoded_JWS = jwk.verify(token_response["access_token"])
53
+ decoded = JSON.parse(decoded_JWS[1])
54
+ decoded
55
+ end
56
+
57
+ # Decrypt JWE
58
+ def decrypt_JWE_response(personal_data_response, private_key)
59
+ jwk = JOSE::JWK.from_pem_file(private_key)
60
+ decrypted_personal_JWE = jwk.block_decrypt(personal_data_response.body)
61
+ decrypted_personal_JWT = jwk.verify(decrypted_personal_JWE[0])
62
+ JSON.parse(decrypted_personal_JWT[1])
63
+ end
64
+
65
+ private
66
+
67
+ # Generate digital fingerprint
68
+ def generate_signature(url, params, method, content_type, app_id, private_key)
69
+ url = url.gsub(".api.gov.sg", ".e.api.gov.sg");
70
+ time_stamp = DateTime.now.strftime('%Q')
71
+ nonce_value = time_stamp+'00'
72
+
73
+ # Initialize header
74
+ apex_headers = {
75
+ "app_id": app_id,
76
+ "nonce": nonce_value,
77
+ "signature_method": "RS256",
78
+ "timestamp": time_stamp
79
+ }
80
+
81
+ if method == 'POST' && content_type!= "application/x-www-form-urlencoded"
82
+ params = {}
83
+ end
84
+
85
+ # Construct baseString
86
+ base_params = apex_headers.merge(params).to_query
87
+ baseString = URI.unescape(method + "&" + url + "&" + base_params)
88
+
89
+ # Sign the baseString with private_key
90
+ pkey = OpenSSL::PKey::RSA.new(File.read(private_key))
91
+ digest = OpenSSL::Digest::SHA256.new
92
+ digital_signature = Base64.strict_encode64(pkey.sign(digest, baseString))
93
+ # v3 auth header
94
+ authorization_header = "PKI_SIGN app_id="+app_id+
95
+ ",timestamp="+time_stamp+",nonce="+nonce_value+
96
+ ",signature_method=RS256,signature="+digital_signature
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,3 @@
1
+ module MyinfoRuby
2
+ VERSION = "1.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: myinfo_ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Manoj More S
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-05-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: jose
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.1'
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 1.1.0
65
+ type: :development
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - "~>"
70
+ - !ruby/object:Gem::Version
71
+ version: '1.1'
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 1.1.0
75
+ - !ruby/object:Gem::Dependency
76
+ name: rest-client
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - '='
80
+ - !ruby/object:Gem::Version
81
+ version: '2.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - '='
87
+ - !ruby/object:Gem::Version
88
+ version: '2.0'
89
+ description: Use this to fetch the personal details from MyInfo API.
90
+ email:
91
+ - lettertomanojmore@gmail.com
92
+ executables: []
93
+ extensions: []
94
+ extra_rdoc_files: []
95
+ files:
96
+ - lib/myinfo_ruby.rb
97
+ - lib/myinfo_ruby/client.rb
98
+ - lib/myinfo_ruby/security.rb
99
+ - lib/myinfo_ruby/version.rb
100
+ homepage: https://github.com/ManojmoreS/myinfo_ruby
101
+ licenses:
102
+ - MIT
103
+ metadata: {}
104
+ post_install_message:
105
+ rdoc_options: []
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 2.0.0
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirements: []
119
+ rubyforge_project:
120
+ rubygems_version: 2.5.2.3
121
+ signing_key:
122
+ specification_version: 4
123
+ summary: Ruby gem for Singapore MyInfo API.
124
+ test_files: []