omniauth-ruesia 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
+ SHA256:
3
+ metadata.gz: 1a44cd7dc2ba7c42cfdb0b16d35871d0ff0109c7927b5fae0338d56cc6b63b94
4
+ data.tar.gz: 15f919a487dd76234cfac35183beb550d5d0a3fb0b518acf07eab19cda184789
5
+ SHA512:
6
+ metadata.gz: 3128af25f1519daea6baf38697e6ee22ae1381b551426672270cd1a587c3a223ac60a0e63a20d5af4e0c75d06b61160e598e107c8870705238d12244b745a2f3
7
+ data.tar.gz: 216ebba99f9eca7cee7d828a69da9cb23b87d52757c7bb890a42075bc5a5ff25e745de684474c627dab86e1fb50be67ddc7d9d70990ea5a07265fbb770df3743
data/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # Ruesia
2
+ `OmniAuth::Strategies::Ruesia` is a simple Rack middleware for authorization in the russian Unified identification and authentication system(ЕСИА). Read the OmniAuth docs for detailed instructions: https://github.com/intridea/omniauth. The `…/v2/ac` resource is used as a technical solution for gathering authentication code and `…/v3/te` for JWT. In order to write `client_secret`, you need to send an http post request to any system that can work with data-hash signing algorithms using mechanisms of certified Russian
3
+ cryptographic means of information protection and a certificate
4
+ of the information system and return json response with signature
5
+ ```
6
+ Request:
7
+ POST /api/sign { test: 'any string' }
8
+
9
+ Response:
10
+ { signature: 'base64urlsafe signature' }
11
+ ```
12
+
13
+ ## Installation
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem "ruesia"
18
+ ```
19
+
20
+ And then execute:
21
+ ```bash
22
+ $ bundle
23
+ ```
24
+
25
+ Or install it yourself as:
26
+ ```bash
27
+ $ gem install ruesia
28
+ ```
29
+ ## Usage
30
+ Here's a quick example, adding the middleware to a Rails app in config/initializers/ruesia.rb:
31
+ ```
32
+ Rails.application.config.middleware.use OmniAuth::Builder do
33
+ provider :ruesia, 'MY_SYSTEM',
34
+ scope: 'fullname email mobile id_doc'
35
+ cert_fingerprint: 'cert hex fingerprint'
36
+ csp_server_url: 'http://192.168.1.195:8080/api/sign'
37
+ client_options:
38
+ site: 'https://esia-portal1.test.gosuslugi.ru'
39
+ end
40
+ ```
41
+
42
+ ## Configuration
43
+ Guidelines for the use of the Unified Identification and Authentication System:
44
+ https://digital.gov.ru/ru/documents/6186/
45
+ |option |comment |
46
+ |---|---|
47
+ |scope |requested access rights - paragraph B4 Table 95 |
48
+ |cert_fingerprint| parameter containing the hash of the certificate (`fingerprint`) of the client system in hex format. To generate it, use http://esia.gosuslugi.ru/public/calc_cert_hash_unix.zip|
49
+ |csp_server_url| url for cms server. We use Faradat to `post` request for `/api/sign`|
50
+
51
+ Add callback request to routes
52
+ ```
53
+ get 'auth/:provider/callback', to: 'api/client/esia#create'
54
+ ```
55
+ ## Contributing
56
+ Contribution directions go here.
57
+
58
+ ## License
59
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ task :default => :spec
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OAuth2
4
+ class EsiaClient < OAuth2::Client
5
+ def auth_code
6
+ @auth_code ||= OAuth2::Strategy::EsiaAuthCode.new(self)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OAuth2
4
+ module Strategy
5
+ class EsiaAuthCode < AuthCode
6
+ def assert_valid_params(_params); end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAuth
4
+ module Strategies
5
+ class Ruesia < OmniAuth::Strategies::OAuth2
6
+ option :name, 'ruesia'
7
+ option :client_options, {
8
+ site: 'https://esia.gosuslugi.ru',
9
+ authorize_url: 'aas/oauth2/v2/ac',
10
+ token_url: 'aas/oauth2/v3/te',
11
+ }
12
+ uid { JWT.decode(access_token.token, nil, false).first['urn:esia:sbj_id'] }
13
+
14
+ info do
15
+ {
16
+ first_name: raw_info['firstName'],
17
+ last_name: raw_info['lastName'],
18
+ middle_name: raw_info['middleName'],
19
+ truster: raw_info['trusted'],
20
+ verifying: raw_info['verifying'],
21
+ r_id_doc: raw_info['r_id_doc'],
22
+ }
23
+ end
24
+
25
+ extra do
26
+ scopes = options.scope.split
27
+ extra_ = {}
28
+ extra_ = extra_.merge(get_email) if scopes.include?('email')
29
+ extra_ = extra_.merge(get_passport) if scopes.include?('id_doc')
30
+ extra_ = extra_.merge(get_mobile) if scopes.include?('mobile')
31
+ extra_
32
+ end
33
+
34
+ def raw_info
35
+ @raw_info ||= access_token.get("/rs/prns/#{uid}")&.parsed
36
+ end
37
+
38
+ private
39
+
40
+ def state
41
+ @state ||= SecureRandom.uuid
42
+ end
43
+
44
+ def authorize_params # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
45
+ options.authorize_params[:state] = state
46
+
47
+ if OmniAuth.config.test_mode
48
+ @env ||= {}
49
+ @env["rack.session"] ||= {}
50
+ end
51
+
52
+ params = options.authorize_params
53
+ .merge(options_for("authorize"))
54
+ .merge(pkce_authorize_params)
55
+
56
+ session["omniauth.pkce.verifier"] = options.pkce_verifier if options.pkce
57
+ session["omniauth.state"] = params[:state]
58
+
59
+ params
60
+ end
61
+
62
+ def request_phase
63
+ redirect client.auth_code.authorize_url(
64
+ authorize_params.merge(
65
+ redirect_uri: callback_url,
66
+ timestamp: timestamp,
67
+ client_certificate_hash: fingerprint,
68
+ client_secret: sign(client_secret_base)
69
+ )
70
+ )
71
+ end
72
+
73
+ def client
74
+ ::OAuth2::EsiaClient.new(
75
+ options.client_id,
76
+ '',
77
+ deep_symbolize(options.client_options)
78
+ )
79
+ end
80
+
81
+ def client_secret_base
82
+ base_secret = [options.client_id, options.scope, timestamp, state, callback_url]
83
+ yield base_secret if block_given?
84
+
85
+ base_secret.join
86
+ end
87
+
88
+ def timestamp
89
+ @timestamp ||= Time.current.strftime('%Y.%m.%d %H:%M:%S %z')
90
+ end
91
+
92
+ def fingerprint
93
+ options.cert_fingerprint
94
+ end
95
+
96
+ def sign(secret)
97
+ secret_base64 = Base64.urlsafe_encode64(secret)
98
+ response = Faraday.post(options.csp_server_url, { text: secret_base64 })
99
+ JSON.parse(response.body)['signature']
100
+ end
101
+
102
+ def build_access_token
103
+ code = request.params['code']
104
+ client.auth_code.get_token(code,
105
+ {
106
+ state: state,
107
+ scope: options.scope,
108
+ timestamp: timestamp,
109
+ redirect_uri: callback_url,
110
+ token_type: 'Bearer',
111
+ client_secret: sign(client_secret_base { |secret| secret << code }),
112
+ client_id: options.client_id,
113
+ client_certificate_hash: fingerprint
114
+ }
115
+ )
116
+ end
117
+
118
+ def get_email
119
+ {
120
+ email: ctts.find { |e| e['type'] == 'EML' }.fetch('value')
121
+ }
122
+ end
123
+
124
+ def get_passport
125
+ {
126
+ passport: access_token
127
+ .get("/rs/prns/#{uid}/docs?embed=(elements)")
128
+ .parsed.fetch('elements', {})
129
+ .find { |e| e['type'] == 'RF_PASSPORT' }
130
+ .to_h
131
+ }
132
+ end
133
+
134
+ def get_mobile
135
+ {
136
+ mobile: ctts
137
+ .find { |e| e['type'] == 'MBT' }
138
+ .fetch('value')
139
+ }
140
+ end
141
+
142
+ def ctts
143
+ @ctts ||= access_token
144
+ .get("/rs/prns/#{uid}/ctts?embed=(elements)")
145
+ .parsed.fetch('elements', {})
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,5 @@
1
+ require 'omniauth'
2
+ require 'omniauth-oauth2'
3
+ require 'oauth2/strategy/esia_auth_code'
4
+ require 'oauth2/esia_client'
5
+ require 'omniauth/strategies/ruesia'
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omniauth-ruesia
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - ''
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-07-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: omniauth
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: omniauth-rails_csrf_protection
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: omniauth-oauth2
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Description of Ruesia.
70
+ email:
71
+ - ''
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - README.md
77
+ - Rakefile
78
+ - lib/oauth2/esia_client.rb
79
+ - lib/oauth2/strategy/esia_auth_code.rb
80
+ - lib/omniauth-ruesia.rb
81
+ - lib/omniauth/strategies/ruesia.rb
82
+ homepage: https://github.com/vysogota0399/omniauth-ruesia
83
+ licenses:
84
+ - MIT
85
+ metadata:
86
+ allowed_push_host: https://rubygems.org
87
+ homepage_uri: https://github.com/vysogota0399/omniauth-ruesia
88
+ source_code_uri: https://github.com/vysogota0399/omniauth-ruesia
89
+ changelog_uri: https://github.com/vysogota0399/omniauth-ruesia/blob/main/CHANGELOG.md
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubygems_version: 3.4.19
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: Summary of Ruesia.
109
+ test_files: []