anoubis_sso_server 0.1.0 → 1.0.2

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.
@@ -0,0 +1,320 @@
1
+ ##
2
+ # OpenID controller class. Defines any OpenID actions according by specification.
3
+ class AnoubisSsoServer::OpenIdController < AnoubisSsoServer::ApplicationController
4
+
5
+ ##
6
+ # Action returns {https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse Provider OpenID configuration}.
7
+ #
8
+ # Default path: /openid/.well-known/openid-configuration
9
+ # @return [Hash] Current OpenID configuration
10
+ def configuration
11
+ result = {
12
+ issuer: sso_server + 'openid/',
13
+ authorization_endpoint: sso_server + 'openid/oauth2/auth',
14
+ token_endpoint: sso_server + 'openid/oauth2/token',
15
+ jwks_uri: sso_server + 'openid/.well-known/jwks.json',
16
+ subject_types_supported: %w[public],
17
+ #response_types_supported: ['code', 'code id_token', 'id_token', 'token id_token', 'token', 'token id_token code'],
18
+ response_types_supported: %w[code],
19
+ claims_supported: %w[sub],
20
+ #grant_types_supported: ['authorization_code', 'implicit', 'client_credentials', 'refresh_token'],
21
+ grant_types_supported: %w[authorization_code],
22
+ response_modes_supported: %w[query fragment],
23
+ userinfo_endpoint: sso_server + 'openid/userinfo',
24
+ scopes_supported: %w[offline_access offline openid'],
25
+ token_endpoint_auth_methods_supported: %w[client_secret_post client_secret_basic private_key_jwt none],
26
+ userinfo_signing_alg_values_supported: %w[none RS256],
27
+ id_token_signing_alg_values_supported: %w[RS256],
28
+ request_parameter_supported: true,
29
+ request_uri_parameter_supported: true,
30
+ require_request_uri_registration: true,
31
+ claims_parameter_supported: false,
32
+ revocation_endpoint: sso_server + 'openid/oauth2/revoke',
33
+ backchannel_logout_supported: true,
34
+ backchannel_logout_session_supported: true,
35
+ frontchannel_logout_supported: true,
36
+ frontchannel_logout_session_supported: true,
37
+ end_session_endpoint: sso_server + 'openid/oauth2/sessions/logout',
38
+ request_object_signing_alg_values_supported: %w[RS256 none],
39
+ code_challenge_methods_supported: %w[plain S256]
40
+ }
41
+
42
+ render json: result
43
+ end
44
+
45
+ ##
46
+ # Action returns OpenID JWKs.
47
+ #
48
+ # Default path: /openid/.well-known/jwks.json
49
+ # @return [Hash] Current JWKs
50
+ def jwks
51
+ begin
52
+ jwks_cache = JSON.parse(self.redis.get("#{redis_prefix}jwks"),{ symbolize_names: true })
53
+ rescue StandardError => e
54
+ jwks_cache = generate_jwks
55
+ end
56
+
57
+ redis.set "#{redis_prefix}jwks", jwks_cache.to_json, ex: 3600
58
+
59
+ render json: jwks_cache
60
+ end
61
+
62
+ ##
63
+ # Action for check user authorization for current browser.
64
+ def auth
65
+ result = {
66
+ result: -1
67
+ }
68
+
69
+ params[:prompt] = 'is' unless params.key? :prompt
70
+ params[:prompt] = 'is' if params[:prompt] != 'none'
71
+
72
+ err = check_basic_parameters
73
+
74
+ if err
75
+ result[:message] = err
76
+ return render(json: result)
77
+ end
78
+
79
+ sign = params[:redirect_uri].index('?') ? '&' : '?'
80
+
81
+ err = check_listed_parameters %w[response_type scope code_challenge code_challenge_method state]
82
+
83
+ if err
84
+ result[:message] = err
85
+ return if redirect_to_uri result[:message], sign
86
+ return render(json: result)
87
+ end
88
+
89
+ unless %w[code].include? params[:response_type]
90
+ result[:message] = I18n.t('anoubis.errors.is_not_correct', title: 'response_type')
91
+ return if redirect_to_uri result[:message], sign
92
+ return render(json: result)
93
+ end
94
+
95
+ scopes = params[:scope].split(' ')
96
+
97
+ params[:code_challenge_method] = params[:code_challenge_method].downcase
98
+ unless %w[s256].include? params[:code_challenge_method]
99
+ result[:message] = I18n.t('anoubis.errors.is_not_correct', title: 'code_challenge_method')
100
+ return if self.redirect_to_uri result[:message], sign
101
+ return render(json: result)
102
+ end
103
+
104
+ if params[:state].length < 6
105
+ result[:message] = I18n.t('anoubis.errors.less_than', title: 'state', size: 6)
106
+ return if self.redirect_to_uri result[:message], sign
107
+ return render(json: result)
108
+ end
109
+
110
+ original_url = request.url[8..]
111
+ original_url = original_url[(original_url.index('/') + 1)..]
112
+
113
+ code = SecureRandom.uuid
114
+ code_hash = {
115
+ scope: scopes,
116
+ code_challenge: params[:code_challenge],
117
+ request_uri: params[:redirect_uri],
118
+ state: params[:state],
119
+ client_id: params[:client_id],
120
+ original_url: sso_server + original_url
121
+ }
122
+
123
+ session = self.get_oauth_session
124
+
125
+ if session
126
+ user = get_user_by_uuid session[:uuid]
127
+
128
+ if user
129
+ code_hash[:uuid] = user.uuid
130
+ redis.set("#{redis_prefix}code:#{code}", code_hash.to_json, ex: 6000)
131
+ redirect_to "#{params[:redirect_uri]}#{sign}state=#{params[:state]}&scope=#{params[:scope]}&code=#{code}", { allow_other_host: true }
132
+ return
133
+ else
134
+ redis.del("#{redis_prefix}session:#{cookies[:oauth_session]}")
135
+ cookies[:oauth_session] = nil
136
+ end
137
+ end
138
+
139
+ result[:message] = I18n.t('anoubis.errors.login_required')
140
+
141
+ if params[:prompt] == 'none'
142
+ redirect_to params[:redirect_uri] + sign + 'error=' + ERB::Util.url_encode(result[:message]), { allow_other_host: true }
143
+ return
144
+ end
145
+
146
+ url = sso_login_url
147
+ url += url.index('?') ? '&' : '?'
148
+ redis.set("#{redis_prefix}login_code:#{code}", code_hash.to_json, ex: 3600)
149
+ redirect_to "#{url}code=#{code}", { allow_other_host: true }
150
+ end
151
+
152
+ ##
153
+ # Action makes access token based on defined parameters
154
+ def access_token
155
+ result = {
156
+ result: -1
157
+ }
158
+
159
+ params[:prompt] == 'yes'
160
+
161
+ err = check_basic_parameters
162
+
163
+ if err
164
+ result[:message] = err
165
+ return render(json: result)
166
+ end
167
+
168
+ err = check_listed_parameters %w[scope code code_verifier grant_type]
169
+
170
+ if err
171
+ result[:message] = err
172
+ return render(json: result)
173
+ end
174
+
175
+ begin
176
+ code = JSON.parse(redis.get("#{redis_prefix}code:#{params[:code]}"),{ symbolize_names: true })
177
+ rescue
178
+ code = nil
179
+ end
180
+
181
+ if !code || code.class != Hash
182
+ result[:message] = I18n.t('anoubis.errors.is_not_correct', title: 'code')
183
+ return render(json: result)
184
+ end
185
+
186
+ str = Digest::SHA256.base64digest(params[:code_verifier]).tr("+/", "-_").tr("=", "")
187
+
188
+ if code[:code_challenge] != str
189
+ result[:message] = I18n.t('anoubis.errors.is_not_correct', title: 'code_verifier')
190
+ return render(json: result)
191
+ end
192
+
193
+ if code[:request_uri] != params[:redirect_uri]
194
+ result[:error] = I18n.t('anoubis.errors.is_not_correct', title: 'request_uri')
195
+ return render(json: result)
196
+ end
197
+
198
+ header = {
199
+ alg: "RS256",
200
+ kid: "public:#{current_system.public}",
201
+ typ: "JWT"
202
+ }
203
+
204
+ user = get_user_by_uuid code[:uuid]
205
+
206
+ payload = {
207
+ aud: [],
208
+ client_id: current_system.uuid,
209
+ exp: Time.now.utc.to_i + current_system.ttl,
210
+ ext: {},
211
+ iat: Time.now.utc.to_i,
212
+ nbf: Time.now.utc.to_i,
213
+ iss: "#{sso_server}openid/",
214
+ jti: SecureRandom.uuid,
215
+ sub: SecureRandom.uuid,
216
+ scp: []
217
+ }
218
+
219
+ keys = JWT::JWK.import(current_system.jwk)
220
+
221
+ user_payload = {
222
+ aud: [current_system.public],
223
+ auth_time: Time.now.utc.to_i,
224
+ exp: Time.now.utc.to_i + current_system.ttl,
225
+ iss: "#{sso_server}openid/",
226
+ jti: SecureRandom.uuid,
227
+ sid: SecureRandom.uuid,
228
+ sub: SecureRandom.uuid,
229
+ iat: Time.now.utc.to_i,
230
+ rat: Time.now.utc.to_i - 1
231
+ }
232
+
233
+ user_payload[:email] = user.email if code[:scope].include? 'email'
234
+
235
+ if code[:scope].include? 'profile'
236
+ user_payload[:name] = user.name
237
+ user_payload[:surname] = user.surname
238
+ end
239
+
240
+ result = {
241
+ access_token: JWT.encode(payload, keys.keypair, 'RS256', header),
242
+ expires_in: current_system.ttl,
243
+ scope: code[:scope],
244
+ token_type: 'bearer',
245
+ id_token: JWT.encode(user_payload, keys.keypair, 'RS256', header),
246
+ }
247
+
248
+ token_hash = {
249
+ uuid: user.uuid
250
+ }
251
+
252
+ self.redis.set("#{redis_prefix}token:#{result[:access_token]}", token_hash.to_json, ex: current_system.ttl)
253
+ self.redis.del("#{redis_prefix}code:#{params[:code]}")
254
+
255
+ options
256
+
257
+ render json: result
258
+ end
259
+
260
+ ##
261
+ # Clear default session
262
+ def logout
263
+ redis.del("#{redis_prefix}session:#{cookies[:oauth_session]}")
264
+ cookies[:oauth_session] = nil
265
+ redirect_to sso_login_url, { allow_other_host: true }
266
+ end
267
+
268
+ ##
269
+ # Check basic oauth parameters (client_id, redirect_uri)
270
+ def check_basic_parameters
271
+ return I18n.t('anoubis.errors.is_not_defined', title: 'client_id') unless params.key? :client_id
272
+
273
+ @current_system = self.get_current_system params[:client_id]
274
+
275
+ return I18n.t('anoubis.errors.is_not_correct', title: 'client_id') unless current_system
276
+
277
+ return I18n.t('anoubis.errors.is_not_defined', title: 'redirect_uri') unless params.key? :redirect_uri
278
+
279
+ return I18n.t('anoubis.errors.is_not_correct', title: 'redirect_uri') unless current_system.request_uri.include? params[:redirect_uri]
280
+
281
+ nil
282
+ end
283
+
284
+ ##
285
+ # Check if page should be redirected to url
286
+ # @param error [String] Error message
287
+ # @param sign [String] Redirect url sign (? or &)
288
+ # @return [Boolean] return 'true' if page should be redirected
289
+ def redirect_to_uri(error, sign)
290
+ if params[:prompt] == 'none'
291
+ redirect_to params[:redirect_uri] + sign + 'error=' + ERB::Util.url_encode(error), { allow_other_host: true }
292
+ return true
293
+ end
294
+
295
+ false
296
+ end
297
+
298
+ ##
299
+ # Procedure generates keys according by used systems. Data is loaded from {AnoubisSsoServer::System}.
300
+ # @return [Hash] Hash ow JWK keys
301
+ def generate_jwks
302
+ result = {
303
+ keys: []
304
+ }
305
+
306
+ AnoubisSsoServer::System.where(state: 'opened').each do |sys|
307
+ key = {
308
+ use: 'sig',
309
+ kty: sys.jwk[:kty],
310
+ kid: "public:#{sys.uuid}",
311
+ alg: 'RS256',
312
+ n: sys.jwk[:n],
313
+ e: sys.jwk[:e]
314
+ }
315
+ result[:keys].push key
316
+ end
317
+
318
+ result
319
+ end
320
+ end
@@ -0,0 +1,5 @@
1
+ ##
2
+ # Main Active Record object inherited from {https://www.rubydoc.info/gems/anoubis/Anoubis/ApplicationRecord Anoubis::ApplicationRecord}
3
+ class AnoubisSsoServer::ApplicationRecord < Anoubis::ApplicationRecord
4
+ self.abstract_class = true
5
+ end
@@ -0,0 +1,68 @@
1
+ ##
2
+ # Default System class
3
+ class AnoubisSsoServer::System < AnoubisSsoServer::ApplicationRecord
4
+ self.table_name = 'systems'
5
+
6
+ before_validation :before_validation_sso_server_system_on_create, on: :create
7
+ before_save :before_save_sso_server_system
8
+ after_save :after_save_sso_server_system
9
+ after_destroy :after_destroy_sso_server_system
10
+
11
+ validates :title, presence: true, length: { maximum: 100 }
12
+ validates :uuid, presence: true, length: { maximum: 40 }, uniqueness: { case_sensitive: true }
13
+ validates :public, presence: true, length: { maximum: 40 }, uniqueness: { case_sensitive: true }
14
+
15
+ enum state: { opened: 0, hidden: 1 }
16
+
17
+ ##
18
+ # Fires before create system. Procedure generates public and private UUID and RSA keypair
19
+ def before_validation_sso_server_system_on_create
20
+ self.uuid = setup_private_system_id
21
+ self.public = setup_public_system_id unless public
22
+ self.request_uri = [] unless request_uri
23
+
24
+ keys = JWT::JWK.new(OpenSSL::PKey::RSA.new(2048))
25
+ self.jwk = keys.export include_private: true
26
+ end
27
+
28
+ ##
29
+ # Procedure setup private user identifier. Procedure can be redefined.
30
+ # @return [String] public user identifier
31
+ def setup_private_system_id
32
+ SecureRandom.uuid
33
+ end
34
+
35
+ ##
36
+ # Procedure setup public user identifier. Used for open API. Procedure can be redefined.
37
+ # @return [String] public user identifier
38
+ def setup_public_system_id
39
+ SecureRandom.uuid
40
+ end
41
+
42
+ ##
43
+ # Returns JWK information
44
+ # @return [Hash] JWK information
45
+ def jwk
46
+ @jwk ||= super.deep_symbolize_keys!
47
+ end
48
+
49
+ ##
50
+ # Fires before System was saved to SQL database. Delete old system cache if public identifier was changed.
51
+ def before_save_sso_server_system
52
+ redis.del "#{redis_prefix}system:#{public_was}" if public_was && public != public_was
53
+ end
54
+
55
+ ##
56
+ # Fires after System was saved to SQL database and setup system cache in Redis database for fast access.
57
+ def after_save_sso_server_system
58
+ redis.set("#{redis_prefix}system:#{public}", { uuid: uuid, public: public, request_uri: request_uri, jwk: jwk, ttl: ttl }.to_json )
59
+ redis.del "#{redis_prefix}jwks"
60
+ end
61
+
62
+ ##
63
+ # Fires after System was destroyed. Clears all systems caches.
64
+ def after_destroy_sso_server_system
65
+ redis.del "#{redis_prefix}system:#{public}"
66
+ redis.del "#{redis_prefix}jwks"
67
+ end
68
+ end
@@ -0,0 +1,112 @@
1
+ ##
2
+ # Default user class
3
+ class AnoubisSsoServer::User < AnoubisSsoServer::ApplicationRecord
4
+ self.table_name = 'users'
5
+
6
+ has_secure_password
7
+ validates :email, presence: true
8
+
9
+ before_validation :before_validation_sso_server_user_on_create, on: :create
10
+ before_save :before_save_sso_server_user
11
+ after_destroy :after_destroy_sso_server_user
12
+
13
+ ## Regexp validation mask for email
14
+ VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
15
+
16
+ validates :email, presence: true, length: { maximum: 50 },
17
+ format: { with: VALID_EMAIL_REGEX },
18
+ uniqueness: { case_sensitive: true }
19
+
20
+ validates :name, presence: true, length: { maximum: 100 }
21
+ validates :surname, presence: true, length: { maximum: 100 }
22
+
23
+ validates :password, length: { in: 5..30 }, on: [:create]
24
+ validates :password, length: { in: 5..30 }, on: [:update], if: :password_changed?
25
+ validates :password_confirmation, length: { in: 5..30 }, on: [:create]
26
+ validates :password_confirmation, length: { in: 5..30 }, on: [:update], if: :password_changed?
27
+
28
+ validates :uuid, presence: true, length: { maximum: 40 }, uniqueness: { case_sensitive: true }
29
+ validates :public, presence: true, length: { maximum: 40 }, uniqueness: { case_sensitive: true }
30
+
31
+ ##
32
+ # Fires before create any User on the server. Procedure generates internal UUID and setup timezone to GMT.
33
+ # Public user identifier is generated also if not defined.
34
+ def before_validation_sso_server_user_on_create
35
+ self.uuid = setup_private_user_id
36
+ self.public = setup_public_user_id unless public
37
+
38
+ self.timezone = 'GMT' if !self.timezone
39
+ end
40
+
41
+ ##
42
+ # Procedure setup private user identifier. Procedure can be redefined.
43
+ # @return [String] public user identifier
44
+ def setup_private_user_id
45
+ SecureRandom.uuid
46
+ end
47
+
48
+ ##
49
+ # Procedure setup public user identifier. Used for open API. Procedure can be redefined.
50
+ # @return [String] public user identifier
51
+ def setup_public_user_id
52
+ SecureRandom.uuid
53
+ end
54
+
55
+ ##
56
+ # Fires before save User data to database.
57
+ # Procedure setup email, timezone and call procedure for clear Redis cache data for the user.
58
+ def before_save_sso_server_user
59
+ self.timezone = 'GMT' if !self.timezone
60
+ self.email = self.email.downcase
61
+ self.clear_cache
62
+ end
63
+
64
+ ##
65
+ # Fires before delete User from database.
66
+ # Procedure call procedure for clear Redis cache data for the user.
67
+ def after_destroy_sso_server_user
68
+ clear_cache
69
+ end
70
+
71
+ ##
72
+ # Procedure saves cached User model data to Redis database for improve access speed.
73
+ def save_cache
74
+ redis.set("#{redis_prefix}user:#{uuid}", self.to_json(except: [:password_digest])) if redis
75
+ end
76
+
77
+ ##
78
+ # Procedure clear cached User model data.
79
+ def clear_cache
80
+ redis.del("#{redis_prefix}user:#{uuid}") if redis
81
+ end
82
+
83
+ ##
84
+ # Procedure checks if password was changed.
85
+ # @return [Boolean] return true if password was changed
86
+ def password_changed?
87
+ !password.blank?
88
+ end
89
+
90
+ ##
91
+ # Procedure returns User model data from the cache or database. If user data isn't present in cache then call procedure that cache User model data.
92
+ # @param uuid [String] - User private UUID
93
+ # @return [Hash] User data
94
+ def self.load_cache(uuid)
95
+ begin
96
+ data = JSON.parse self.redis.get("#{self.redis_prefix}user:#{uuid}"), { symbolize_names: true }
97
+ rescue
98
+ data = nil
99
+ end
100
+
101
+ unless data
102
+ user = self.where(uuid: uuid).first
103
+
104
+ return nil unless user
105
+
106
+ user.save_cache
107
+ data = JSON.parse(user.to_json(except: [:password_digest]), { symbolize_names: true })
108
+ end
109
+
110
+ data
111
+ end
112
+ end
@@ -0,0 +1,14 @@
1
+ en:
2
+ anoubis:
3
+ errors:
4
+ incorrect_login: "Incorrect login or password"
5
+ system_not_defined: "SSO system is not defined in Rails.configuration.anoubis_sso_system"
6
+ session_expired: "Session expired"
7
+ incorrect_user: "Incorrect user"
8
+ is_not_defined: "%{title} isn't defined"
9
+ is_not_correct: "%{title} isn't correct"
10
+ less_than: "%{title} length should be %{size} or more symbols"
11
+ login_required: "Login required"
12
+ fields:
13
+ login: "Login not defined"
14
+ password: "Password not defined"
@@ -0,0 +1,14 @@
1
+ ru:
2
+ anoubis:
3
+ errors:
4
+ incorrect_login: "Некорректный логин или пароль"
5
+ system_not_defined: "SSO система не определена в Rails.configuration.anoubis_sso_system"
6
+ session_expired: "Сессия завершена"
7
+ incorrect_user: "Некорректный пользователь"
8
+ is_not_defined: "Переменная %{title} не определена"
9
+ is_not_correct: "Переменная %{title} некорректна"
10
+ less_than: "Длина переменной %{title} должна быть %{size} или более символов"
11
+ login_required: "Требуется логин"
12
+ fields:
13
+ login: "Логин не задан"
14
+ password: "Пароль не задан"
data/config/routes.rb ADDED
@@ -0,0 +1,23 @@
1
+ ##
2
+ # Defines default routes
3
+ AnoubisSsoServer::Engine.routes.draw do
4
+ Rails.application.routes.draw do
5
+ scope path: 'api', defaults: { format: 'json' } do
6
+ scope path: ':version' do
7
+ get 'login', to: 'anoubis_sso_server/main#login', as: 'api_internal_login'
8
+ get 'auth', to: 'anoubis_sso_server/main#auth', as: 'api_internal_auth'
9
+ get 'dashboard', to: 'anoubis_sso_server/index#dashboard'
10
+ get 'menu', to: 'anoubis_sso_server/index#menu'
11
+ end
12
+ end
13
+
14
+ scope path: 'openid', defaults: { format: 'json' } do
15
+ get '.well-known/openid-configuration', to: 'anoubis_sso_server/open_id#configuration', as: 'openid_configuration'
16
+ get '.well-known/jwks.json', to: 'anoubis_sso_server/open_id#jwks', as: 'openid_jwks'
17
+ get 'oauth2/auth', to: 'anoubis_sso_server/open_id#auth', as: 'oauth_auth'
18
+ post 'oauth2/token', to: 'anoubis_sso_server/open_id#access_token', as: 'oauth_token'
19
+ options 'oauth2/token', to: 'anoubis_sso_server/application#options', as: nil
20
+ get 'oauth2/logout', to: 'anoubis_sso_server/open_id#logout', as: 'oauth_logout'
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ class CreateAnoubisSsoServerUsers < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :users do |t|
4
+ t.string :email, limit: 100, null: false
5
+ t.string :name, limit: 100, null: false
6
+ t.string :surname, limit: 100, null: false
7
+ t.string :timezone, limit: 30, null: false
8
+ t.string :locale, limit: 10, null: false, default: 'ru-RU'
9
+ t.string :password_digest, limit: 60, null: false
10
+ t.string :uuid, limit: 40, null: false
11
+ t.string :public, limit: 40, null: false
12
+
13
+ t.timestamps
14
+ end
15
+ add_index :users, [:email], unique: true
16
+ add_index :users, [:uuid], unique: true
17
+ add_index :users, [:public], unique: true
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ class CreateAnoubisSsoServerSystems < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :systems do |t|
4
+ t.string :title, limit: 100, null: false
5
+ t.string :uuid, limit: 40, null: false
6
+ t.string :public, limit: 40, null: false
7
+ t.integer :ttl, default: 3600, null: false
8
+ t.integer :state, default: 0, null: false
9
+ t.json :jwk
10
+ t.json :request_uri
11
+
12
+ t.timestamps
13
+ end
14
+ add_index :systems, [:uuid], unique: true
15
+ add_index :systems, [:public], unique: true
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ module AnoubisSsoServer
2
+ ##
3
+ # Main AnoubisSsoServer Engine class
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace AnoubisSsoServer
6
+ config.generators.api_only = true
7
+
8
+ config.generators do |g|
9
+ g.test_framework :rspec
10
+ g.fixture_replacement :factory_bot, :dir => 'spec/factories'
11
+ end
12
+ end
13
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AnoubisSsoServer
4
- VERSION = "0.1.0"
4
+ ## Library version
5
+ VERSION = "1.0.2"
5
6
  end
@@ -1,8 +1,9 @@
1
- # frozen_string_literal: true
2
-
3
1
  require_relative "anoubis_sso_server/version"
2
+ require_relative "anoubis_sso_server/engine"
4
3
 
4
+ ## Main module of library for create basic SSO Server based on OAUTH authentication.
5
5
  module AnoubisSsoServer
6
- class Error < StandardError; end
7
- # Your code goes here...
6
+ ## Default error class
7
+ class Error < StandardError;
8
+ end
8
9
  end
@@ -1,4 +1,10 @@
1
1
  module AnoubisSsoServer
2
2
  VERSION: String
3
3
  # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+
5
+ class User
6
+ attr_accessor email: String
7
+ attr_accessor name: String
8
+ attr_accessor surname: String
9
+ end
4
10
  end