gamora 0.8.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d898db3b0a39bb00099a8912953f6dcdfda11cddbb38eccfe72a798986a4dbb9
4
- data.tar.gz: 943a549def5cc2b014fe7200cb72cfdfd00acef025b2db74731cce0eecfa5927
3
+ metadata.gz: 62fcee90396875db0256e297c90dda80546c3268cfecdd4f0e53b7f3c4e68ba8
4
+ data.tar.gz: '093571bfd8706530b474c1bf9d046db56cd5a0a53820546afc88f274f528a6a6'
5
5
  SHA512:
6
- metadata.gz: a71915324e16d094db423133defdfa83f21a4451eb32ac468d4d2be35eb0768493e6cba8ed48c2efea746f4219e406751159c3d5eac500fe3e52f1e7c94c99d9
7
- data.tar.gz: 86a1b5282d7bfc407264f971bc3f5ba2c6f756028d5afdb857e069ede7cc9787c9ad4b291b9e4fcf9365bc9a4f4e26e977a3b13f4a870b1c32c0b24bd6ba95e4
6
+ metadata.gz: '0888b96495e6e55f0cc49512a9ec0c8d73d67e839a2dae0207bdb81728032e46436b8a3ce0cdf1daa1352d5ae849a4a723a28fe29d9c6c615b5efbf6dd33cc8c'
7
+ data.tar.gz: 3c00a26cce8ad257170853ffc0c6c9fd3a355372f401881b470cb9cdb86d0a94e43b401e5ac94024979faa3636969fc33ec7429f033287d006a6ff5bfa8360aa
data/README.md CHANGED
@@ -115,6 +115,38 @@ Optionally, if you want to do something different when authentication
115
115
  fails, you just need to override the `user_authentication_failed!`
116
116
  method in you controller and customize it as you wish.
117
117
 
118
+ ## Cross-Client Identity
119
+
120
+ By default, gamora will accept only access tokens that were generating
121
+ with the `client_id` in the configuration. If access tokens coming from
122
+ other clients have to be accepted, make sure to add their client ids to
123
+ the `whitelisted_clients` config option.
124
+
125
+ ```ruby
126
+ Gamora.setup do |config|
127
+ ...
128
+
129
+ config.whitelisted_clients = ["OTHER_CLIENT_ID"]
130
+ end
131
+ ```
132
+
133
+ ## Caching
134
+
135
+ In order to avoid performing requests to the IDP on each request in the
136
+ application, it is possible to set a caching time for introspection and
137
+ userinfo endpoints. Make sure to not have a too long expiration time for
138
+ `introspect_cache_expires_in` but not too short to impact the application
139
+ performance, it is a balance.
140
+
141
+ ```ruby
142
+ Gamora.setup do |config|
143
+ ...
144
+
145
+ config.userinfo_cache_expires_in = 10.minute
146
+ config.introspect_cache_expires_in = 5.seconds
147
+ end
148
+ ```
149
+
118
150
  ## Development
119
151
 
120
152
  After checking out the repo, run `bin/setup` to install dependencies. Then,
@@ -7,6 +7,7 @@ module Gamora
7
7
  sub: :id,
8
8
  roles: :roles,
9
9
  email: :email,
10
+ username: :username,
10
11
  given_name: :first_name,
11
12
  family_name: :last_name,
12
13
  phone_number: :phone_number,
@@ -15,9 +16,15 @@ module Gamora
15
16
  }.freeze
16
17
 
17
18
  def authenticate_user!
19
+ return authentication_failed! unless access_token.present?
20
+
21
+ token_data = introspect_access_token(access_token)
22
+ return authentication_failed! unless valid_token_data?(token_data)
23
+
18
24
  claims = resource_owner_claims(access_token)
19
- assign_current_user_from_claims(claims) if claims.present?
20
- validate_authentication!
25
+ return authentication_failed! unless claims.present?
26
+
27
+ assign_current_user_from_claims(claims)
21
28
  end
22
29
 
23
30
  def current_user
@@ -26,21 +33,29 @@ module Gamora
26
33
 
27
34
  private
28
35
 
29
- def validate_authentication!
36
+ def access_token
30
37
  raise NotImplementedError
31
38
  end
32
39
 
33
- def access_token
40
+ def authentication_failed!
34
41
  raise NotImplementedError
35
42
  end
36
43
 
37
- def user_authentication_failed!
38
- raise NotImplementedError
44
+ def valid_token_data?(token_data)
45
+ token_data[:active] && whitelisted_client?(token_data[:client_id])
46
+ end
47
+
48
+ def whitelisted_client?(client_id)
49
+ whitelisted_clients.include?(client_id)
50
+ end
51
+
52
+ def whitelisted_clients
53
+ Configuration.whitelisted_clients | [Configuration.client_id]
39
54
  end
40
55
 
41
56
  def assign_current_user_from_claims(claims)
42
- attrs = user_attributes_from_claims(claims)
43
- @current_user = User.new(attrs)
57
+ attributes = user_attributes_from_claims(claims)
58
+ @current_user = User.new(attributes)
44
59
  end
45
60
 
46
61
  def user_attributes_from_claims(claims)
@@ -48,27 +63,29 @@ module Gamora
48
63
  end
49
64
 
50
65
  def resource_owner_claims(access_token)
51
- return {} if access_token.blank?
52
-
53
- resource_owner_claims!(access_token)
54
- end
66
+ cache_key = cache_key(:userinfo, access_token)
67
+ expires_in = Configuration.userinfo_cache_expires_in
55
68
 
56
- def resource_owner_claims!(access_token)
57
- Rails.cache.fetch(cache_key(access_token), cache_options) do
69
+ Rails.cache.fetch(cache_key, { expires_in: expires_in }) do
58
70
  oauth_client.userinfo(access_token)
59
71
  end
60
72
  end
61
73
 
62
- def oauth_client
63
- Client.from_config
74
+ def introspect_access_token(access_token)
75
+ cache_key = cache_key(:introspect, access_token)
76
+ expires_in = Configuration.introspect_cache_expires_in
77
+
78
+ Rails.cache.fetch(cache_key, { expires_in: expires_in }) do
79
+ oauth_client.introspect(access_token)
80
+ end
64
81
  end
65
82
 
66
- def cache_options
67
- { expires_in: Configuration.userinfo_cache_expires_in }
83
+ def cache_key(context, access_token)
84
+ "gamora:#{context}:#{Digest::SHA256.hexdigest(access_token)}"
68
85
  end
69
86
 
70
- def cache_key(access_token)
71
- "userinfo:#{Digest::SHA256.hexdigest(access_token)}"
87
+ def oauth_client
88
+ Client.from_config
72
89
  end
73
90
  end
74
91
  end
@@ -7,12 +7,6 @@ module Gamora
7
7
 
8
8
  private
9
9
 
10
- def validate_authentication!
11
- return if current_user.present?
12
-
13
- user_authentication_failed!
14
- end
15
-
16
10
  def access_token
17
11
  pattern = /^Bearer /
18
12
  header = request.headers["Authorization"]
@@ -21,7 +15,7 @@ module Gamora
21
15
  header.gsub(pattern, "")
22
16
  end
23
17
 
24
- def user_authentication_failed!
18
+ def authentication_failed!
25
19
  render json: { error: "Access token invalid" }, status: :unauthorized
26
20
  end
27
21
  end
@@ -11,18 +11,12 @@ module Gamora
11
11
 
12
12
  private
13
13
 
14
- def validate_authentication!
15
- return if current_user.present?
16
-
17
- session["gamora.origin"] = request.original_url
18
- user_authentication_failed!
19
- end
20
-
21
14
  def access_token
22
15
  session[:access_token]
23
16
  end
24
17
 
25
- def user_authentication_failed!
18
+ def authentication_failed!
19
+ session["gamora.origin"] = request.original_url
26
20
  redirect_to gamora.authentication_path
27
21
  end
28
22
  end
data/lib/gamora/client.rb CHANGED
@@ -20,13 +20,25 @@ module Gamora
20
20
  token_method: Configuration.token_method,
21
21
  redirect_uri: Configuration.redirect_uri,
22
22
  userinfo_url: Configuration.userinfo_url,
23
- authorize_url: Configuration.authorize_url
23
+ authorize_url: Configuration.authorize_url,
24
+ introspect_url: Configuration.introspect_url
24
25
  }
25
26
  end
26
27
  end
27
28
 
28
29
  def userinfo(access_token)
29
- response = userinfo_request(access_token)
30
+ params = userinfo_params(access_token)
31
+ opts = request_options(params)
32
+ response = request(:post, options[:userinfo_url], opts)
33
+ JSON.parse(response.body).symbolize_keys
34
+ rescue OAuth2::Error
35
+ {}
36
+ end
37
+
38
+ def introspect(access_token)
39
+ params = introspect_params(access_token)
40
+ opts = request_options(params)
41
+ response = request(:post, options[:introspect_url], opts)
30
42
  JSON.parse(response.body).symbolize_keys
31
43
  rescue OAuth2::Error
32
44
  {}
@@ -34,15 +46,22 @@ module Gamora
34
46
 
35
47
  private
36
48
 
37
- def userinfo_request(access_token)
38
- opts = userinfo_request_options(access_token)
39
- request(:post, options[:userinfo_url], opts)
49
+ def request_options(params)
50
+ {
51
+ body: params.to_json,
52
+ headers: { "Content-Type": "application/json" }
53
+ }
40
54
  end
41
55
 
42
- def userinfo_request_options(access_token)
56
+ def userinfo_params(access_token)
57
+ { access_token: access_token }
58
+ end
59
+
60
+ def introspect_params(access_token)
43
61
  {
44
- body: { access_token: access_token }.to_json,
45
- headers: { "Content-Type": "application/json" }
62
+ token: access_token,
63
+ client_id: Configuration.client_id,
64
+ client_secret: Configuration.client_secret
46
65
  }
47
66
  end
48
67
  end
@@ -6,8 +6,9 @@ module Gamora
6
6
  mattr_accessor :client_secret, default: nil
7
7
  mattr_accessor :site, default: nil
8
8
  mattr_accessor :token_url, default: "/oauth2/token"
9
- mattr_accessor :authorize_url, default: "/oauth2/authorize"
10
9
  mattr_accessor :userinfo_url, default: "/oauth2/userinfo"
10
+ mattr_accessor :authorize_url, default: "/oauth2/authorize"
11
+ mattr_accessor :introspect_url, default: "/oauth2/introspect"
11
12
  mattr_accessor :token_method, default: :post
12
13
  mattr_accessor :redirect_uri, default: nil
13
14
  mattr_accessor :default_scope, default: "openid profile email"
@@ -16,7 +17,9 @@ module Gamora
16
17
  mattr_accessor :default_branding, default: "amco"
17
18
  mattr_accessor :default_theme, default: "default"
18
19
  mattr_accessor :ui_locales, default: -> { I18n.locale }
19
- mattr_accessor :userinfo_cache_expires_in, default: 0.seconds
20
+ mattr_accessor :userinfo_cache_expires_in, default: 1.minute
21
+ mattr_accessor :introspect_cache_expires_in, default: 0.seconds
22
+ mattr_accessor :whitelisted_clients, default: []
20
23
 
21
24
  def setup
22
25
  yield(self) if block_given?
data/lib/gamora/user.rb CHANGED
@@ -7,6 +7,7 @@ module Gamora
7
7
  attr_accessor :id,
8
8
  :roles,
9
9
  :email,
10
+ :username,
10
11
  :last_name,
11
12
  :first_name,
12
13
  :phone_number,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gamora
4
- VERSION = "0.8.0"
4
+ VERSION = "0.10.0"
5
5
  end
@@ -8,8 +8,9 @@ Gamora.setup do |config|
8
8
 
9
9
  # ===> Optional OAuth2 configuration options and its defaults.
10
10
  # config.token_url = "/oauth2/token"
11
- # config.authorize_url = "/oauth2/authorize"
12
11
  # config.userinfo_url = "/oauth2/userinfo"
12
+ # config.authorize_url = "/oauth2/authorize"
13
+ # config.introspect_url = "/oauth2/introspect"
13
14
  # config.token_method = :post
14
15
  # config.redirect_uri = nil
15
16
  # config.default_scope = "openid profile email"
@@ -18,5 +19,7 @@ Gamora.setup do |config|
18
19
  # config.default_branding = "amco"
19
20
  # config.default_theme = "default"
20
21
  # config.ui_locales = -> { I18n.locale }
21
- # config.userinfo_cache_expires_in = 0.seconds
22
+ # config.userinfo_cache_expires_in = 1.minute
23
+ # config.introspect_cache_expires_in = 0.seconds
24
+ # config.whitelisted_clients = []
22
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gamora
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alejandro Gutiérrez
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-21 00:00:00.000000000 Z
11
+ date: 2023-12-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oauth2