myinfo_ruby 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []