zaikio-oauth_client 0.9.0 → 0.12.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e956bf122053d559c2dd918f4b90a23c10559bc5578442bc26dbe1530a3396b
4
- data.tar.gz: 0d594a45ddf589b03d9e43817885377d963934345fa6cb936b367fe64761e08a
3
+ metadata.gz: 2e0377cbc59afa07b79f540877cbba7c79c0a23240202cdd1e1b0b1f79d21104
4
+ data.tar.gz: 3b99c3429d501c37bb259046159794df88a26789d519a7ec50203961a62cfd43
5
5
  SHA512:
6
- metadata.gz: 38355e0660ea6b2fd2c1b13a23a75626b5d4ae4efd3e64eb21396893e4a2ffc39c34824f373e994f596e4064455a8ad596b63b5e595c16afa844ffcddc90e56a
7
- data.tar.gz: 1cb76ea316682fc4a4394d27ad252cc67496e95fa9034ba7054ad6e9b472a211f78cb1c839c9f4c745b143804c04aef5c85616c4f6f344f378e19bf12420db53
6
+ metadata.gz: f6b9f87688b92771ee8bb9ae1eea9ae9c83a09f393860925698ffa7adf515478b70d1621e438420ae39642a27c8c193a5a8232e1d028c58858428d8835b685e2
7
+ data.tar.gz: 153edcd1c0eb91874efd44ad501c157ad5210da20447064b07d59967593cb59a2e940767f3a3c32943ae550a55e0f537a737bcab4f17fe6d373ff44f049c06f8
data/README.md CHANGED
@@ -133,12 +133,12 @@ redirect_to zaikio_oauth_client.new_subscription_path(plan: "free")
133
133
 
134
134
  #### Session handling
135
135
 
136
- The Zaikio gem engine will set a cookie for the user after a successful OAuth flow: `cookies.encrypted[:zaikio_person_id]`.
136
+ The Zaikio gem engine will set a cookie for the user after a successful OAuth flow: `session[:zaikio_person_id]`.
137
137
 
138
138
  If you are using for example `Zaikio::Hub::Models`, you can use this snippet to set the current user:
139
139
 
140
140
  ```ruby
141
- Current.user ||= Zaikio::Hub::Models::Person.find_by(id: cookies.encrypted[:zaikio_person_id])
141
+ Current.user ||= Zaikio::Hub::Models::Person.find_by(id: session[:zaikio_person_id])
142
142
  ````
143
143
 
144
144
  You can then use `Current.user` anywhere.
@@ -172,7 +172,7 @@ Additionally you can also specify your own redirect handlers in your `Applicatio
172
172
  ```rb
173
173
  class ApplicationController < ActionController::Base
174
174
  def after_approve_path_for(access_token, origin)
175
- cookies.encrypted[:zaikio_person_id] = access_token.bearer_id unless access_token.organization?
175
+ session[:zaikio_person_id] = access_token.bearer_id unless access_token.organization?
176
176
 
177
177
  # Sync data on login
178
178
  Zaikio::Hub.with_token(access_token.token) do
@@ -183,10 +183,15 @@ class ApplicationController < ActionController::Base
183
183
  end
184
184
 
185
185
  def after_destroy_path_for(access_token_id)
186
- cookies.delete :zaikio_person_id
186
+ reset_session
187
187
 
188
188
  main_app.root_path
189
189
  end
190
+
191
+ def error_path_for(error_code, description: nil)
192
+ # Handle error
193
+ main_app.root_path
194
+ end
190
195
  end
191
196
  ```
192
197
 
@@ -234,6 +239,21 @@ class MyControllerTest < ActionDispatch::IntegrationTest
234
239
  end
235
240
  ```
236
241
 
242
+ For system tests (e.g. with a separate browser instance), there's a special helper:
243
+
244
+ ```rb
245
+ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
246
+ include Zaikio::OAuthClient::SystemTestHelper
247
+
248
+ test "does request" do
249
+ person = people(:my_person)
250
+ logged_in_as(person)
251
+
252
+ visit "/"
253
+ end
254
+ end
255
+ ```
256
+
237
257
  #### Authenticated requests
238
258
 
239
259
  Now further requests to the Directory API or to other Zaikio APIs should be made. For this purpose the OAuthClient provides a helper method `with_auth` that automatically fetches an access token from the database, requests a refresh token or creates a new access token via client credentials flow.
@@ -2,15 +2,23 @@ module Zaikio
2
2
  module OAuthClient
3
3
  class SubscriptionsController < ConnectionsController
4
4
  def new
5
- opts = params.permit(:client_name, :state, :plan)
6
- client_name = opts.delete(:client_name)
7
- plan = opts.delete(:plan)
5
+ opts = params.permit(:client_name, :state, :plan, :organization_id)
6
+ opts[:redirect_with_error] = 1
7
+ opts[:state] ||= session[:state] = SecureRandom.urlsafe_base64(32)
8
+
9
+ plan = opts.delete(:plan)
10
+ organization_id = opts.delete(:organization_id)
11
+
12
+ subscription_scope = if organization_id.present?
13
+ "Org/#{organization_id}.subscription_create"
14
+ else
15
+ "Org.subscription_create"
16
+ end
8
17
 
9
- subscription_scope = "Org.subscription_create"
10
18
  subscription_scope << ".#{plan}" if plan.present?
11
19
 
12
20
  redirect_to oauth_client.auth_code.authorize_url(
13
- redirect_uri: approve_url(client_name),
21
+ redirect_uri: approve_url(opts.delete(:client_name)),
14
22
  scope: subscription_scope,
15
23
  **opts
16
24
  )
@@ -0,0 +1,4 @@
1
+ de:
2
+ zaikio:
3
+ oauth_client:
4
+ error_occured: "Beim Login ist ein Fehler aufgetreten: %{error} %{description}. Bitte versuche es nochmal."
@@ -1,6 +1,7 @@
1
1
  en:
2
2
  zaikio:
3
+ oauth_client:
4
+ error_occured: "An error occurred during login: %{error} %{description}. Please try again."
3
5
  forms:
4
6
  optional: Optional
5
7
  learn_more: Learn more
6
-
@@ -1,5 +1,6 @@
1
1
  require "oauth2"
2
2
 
3
+ require "zaikio/oauth_client/error"
3
4
  require "zaikio/oauth_client/engine"
4
5
  require "zaikio/oauth_client/configuration"
5
6
  require "zaikio/oauth_client/authenticatable"
@@ -82,14 +83,25 @@ module Zaikio
82
83
  # Finds the best usable access token. Note that this token may have expired and
83
84
  # would require refreshing.
84
85
  def find_usable_access_token(client_name:, bearer_type:, bearer_id:, requested_scopes:)
85
- Zaikio::AccessToken
86
- .where(audience: client_name)
87
- .usable(
88
- bearer_type: bearer_type,
89
- bearer_id: bearer_id,
90
- requested_scopes: requested_scopes
91
- )
92
- .first
86
+ configuration.logger.debug "Try to fetch token for client_name: #{client_name}, "\
87
+ "bearer #{bearer_type}/#{bearer_id}, requested_scopes: #{requested_scopes}"
88
+
89
+ fetch_access_token = lambda {
90
+ Zaikio::AccessToken
91
+ .where(audience: client_name)
92
+ .usable(
93
+ bearer_type: bearer_type,
94
+ bearer_id: bearer_id,
95
+ requested_scopes: requested_scopes
96
+ )
97
+ .first
98
+ }
99
+
100
+ if configuration.logger.respond_to?(:silence)
101
+ configuration.logger.silence { fetch_access_token.call }
102
+ else
103
+ fetch_access_token.call
104
+ end
93
105
  end
94
106
 
95
107
  def fetch_new_token(client_config:, bearer_type:, bearer_id:, scopes:)
@@ -5,7 +5,9 @@ module Zaikio
5
5
 
6
6
  def new
7
7
  opts = params.permit(:client_name, :show_signup, :force_login, :state)
8
+ opts[:redirect_with_error] = 1
8
9
  client_name = opts.delete(:client_name)
10
+ opts[:state] ||= session[:state] = SecureRandom.urlsafe_base64(32)
9
11
 
10
12
  redirect_to oauth_client.auth_code.authorize_url(
11
13
  redirect_uri: approve_url(client_name),
@@ -15,12 +17,27 @@ module Zaikio
15
17
  end
16
18
 
17
19
  def approve
20
+ if params[:error].present?
21
+ redirect_to send(
22
+ respond_to?(:error_path_for) ? :error_path_for : :default_error_path_for,
23
+ params[:error],
24
+ description: params[:error_description]
25
+ ) and return
26
+ end
27
+
28
+ if session[:state].present? && params[:state] != session[:state]
29
+ return redirect_to send(
30
+ respond_to?(:error_path_for) ? :error_path_for : :default_error_path_for,
31
+ "invalid_state"
32
+ )
33
+ end
34
+
18
35
  access_token = create_access_token
19
36
 
20
- origin = cookies.encrypted[:origin]
21
- cookies.delete :origin
37
+ origin = session[:origin]
38
+ session.delete(:origin)
22
39
 
23
- cookies.encrypted[:zaikio_access_token_id] = access_token.id unless access_token.organization?
40
+ session[:zaikio_access_token_id] = access_token.id unless access_token.organization?
24
41
 
25
42
  redirect_to send(
26
43
  respond_to?(:after_approve_path_for) ? :after_approve_path_for : :default_after_approve_path_for,
@@ -29,8 +46,9 @@ module Zaikio
29
46
  end
30
47
 
31
48
  def destroy
32
- access_token_id = cookies.encrypted[:zaikio_access_token_id]
33
- cookies.delete :zaikio_access_token_id
49
+ access_token_id = session[:zaikio_access_token_id]
50
+ session.delete(:zaikio_access_token_id)
51
+ session.delete(:origin)
34
52
 
35
53
  redirect_to send(
36
54
  respond_to?(:after_destroy_path_for) ? :after_destroy_path_for : :default_after_destroy_path_for,
@@ -77,13 +95,23 @@ module Zaikio
77
95
  end
78
96
 
79
97
  def default_after_approve_path_for(access_token, origin)
80
- cookies.encrypted[:zaikio_person_id] = access_token.bearer_id unless access_token.organization?
98
+ session[:zaikio_person_id] = access_token.bearer_id unless access_token.organization?
81
99
 
82
100
  origin || main_app.root_path
83
101
  end
84
102
 
85
103
  def default_after_destroy_path_for(_access_token_id)
86
- cookies.delete :zaikio_person_id
104
+ session.delete(:origin)
105
+
106
+ main_app.root_path
107
+ end
108
+
109
+ def default_error_path_for(error_code, description: nil)
110
+ raise Zaikio::OAuthClient::InvalidScopesError, description if error_code == "invalid_scope"
111
+
112
+ unless error_code == "access_denied"
113
+ flash[:alert] = I18n.t("zaikio.oauth_client.error_occured", error: error_code, description: description)
114
+ end
87
115
 
88
116
  main_app.root_path
89
117
  end
@@ -13,7 +13,6 @@ module Zaikio
13
13
  }.freeze
14
14
 
15
15
  attr_accessor :host
16
- attr_writer :logger
17
16
  attr_reader :client_configurations, :environment, :around_auth_block,
18
17
  :sessions_controller_name, :connections_controller_name, :subscriptions_controller_name
19
18
 
@@ -23,10 +22,16 @@ module Zaikio
23
22
  @sessions_controller_name = "sessions"
24
23
  @connections_controller_name = "connections"
25
24
  @subscriptions_controller_name = "subscriptions"
25
+ Zaikio::AccessToken.logger = logger
26
26
  end
27
27
 
28
28
  def logger
29
- @logger ||= Logger.new($stdout)
29
+ @logger ||= ActiveSupport::Logger.new($stdout)
30
+ end
31
+
32
+ def logger=(logger)
33
+ @logger = logger
34
+ Zaikio::AccessToken.logger = @logger
30
35
  end
31
36
 
32
37
  def register_client(name)
@@ -0,0 +1,5 @@
1
+ module Zaikio
2
+ module OAuthClient
3
+ class InvalidScopesError < StandardError; end
4
+ end
5
+ end
@@ -0,0 +1,18 @@
1
+ require_relative "./test_helper"
2
+
3
+ module Zaikio
4
+ module OAuthClient
5
+ module SystemTestHelper
6
+ include ::Zaikio::OAuthClient::TestHelper
7
+
8
+ def set_session(key, value)
9
+ visit "/zaikio/oauth_client/test_helper/session?#{{ key: key, id: value }.to_query}"
10
+ end
11
+
12
+ def get_session(key)
13
+ visit "/zaikio/oauth_client/test_helper/get_session?#{{ key: key }.to_query}"
14
+ page.text
15
+ end
16
+ end
17
+ end
18
+ end
@@ -3,13 +3,48 @@ module Zaikio
3
3
  module TestHelper
4
4
  extend ActiveSupport::Concern
5
5
 
6
- def logged_in_as(person)
7
- # We need to manually encrypt the value since the tests cookie jar does not
8
- # support encrypted or signed cookies
9
- encrypted_cookies = ActionDispatch::Request.new(Rails.application.env_config.deep_dup).cookie_jar
10
- encrypted_cookies.encrypted[:zaikio_person_id] = person.id
6
+ class TestSessionController < ActionController::Base
7
+ def show
8
+ if session[params[:key]].nil?
9
+ head :no_content
10
+ else
11
+ render plain: session[params[:key]]
12
+ end
13
+ end
14
+
15
+ def create
16
+ session[params[:key]] = params[:id]
17
+
18
+ head :ok
19
+ end
20
+ end
21
+
22
+ included do
23
+ # This is needed as it is not possible to set sesison values in an ActionDispatch::IntegrationTest
24
+ # This creates a dummy controller to set the session
25
+ Rails.application.routes.disable_clear_and_finalize = true # Keep existing routes
26
+ Rails.application.routes.draw do
27
+ get "/zaikio/oauth_client/test_helper/get_session", to: "zaikio/oauth_client/test_helper/test_session#show"
28
+ get "/zaikio/oauth_client/test_helper/session", to: "zaikio/oauth_client/test_helper/test_session#create"
29
+ end
30
+ end
11
31
 
12
- cookies["zaikio_person_id"] = encrypted_cookies["zaikio_person_id"]
32
+ def get_session(key)
33
+ get "/zaikio/oauth_client/test_helper/get_session", params: { key: key }
34
+
35
+ if response.status == 204
36
+ nil
37
+ else
38
+ response.body
39
+ end
40
+ end
41
+
42
+ def set_session(key, value)
43
+ get "/zaikio/oauth_client/test_helper/session", params: { id: value, key: key }
44
+ end
45
+
46
+ def logged_in_as(person)
47
+ set_session(:zaikio_person_id, person.id)
13
48
  end
14
49
  end
15
50
  end
@@ -1,5 +1,5 @@
1
1
  module Zaikio
2
2
  module OAuthClient
3
- VERSION = "0.9.0".freeze
3
+ VERSION = "0.12.1".freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zaikio-oauth_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.12.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zaikio GmbH
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-13 00:00:00.000000000 Z
11
+ date: 2021-04-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -86,20 +86,20 @@ dependencies:
86
86
  requirements:
87
87
  - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: 0.2.1
89
+ version: '0.5'
90
90
  - - "<"
91
91
  - !ruby/object:Gem::Version
92
- version: 0.5.0
92
+ version: '2.0'
93
93
  type: :runtime
94
94
  prerelease: false
95
95
  version_requirements: !ruby/object:Gem::Requirement
96
96
  requirements:
97
97
  - - ">="
98
98
  - !ruby/object:Gem::Version
99
- version: 0.2.1
99
+ version: '0.5'
100
100
  - - "<"
101
101
  - !ruby/object:Gem::Version
102
- version: 0.5.0
102
+ version: '2.0'
103
103
  - !ruby/object:Gem::Dependency
104
104
  name: pg
105
105
  requirement: !ruby/object:Gem::Requirement
@@ -150,6 +150,7 @@ files:
150
150
  - app/jobs/zaikio/cleanup_access_tokens_job.rb
151
151
  - app/models/zaikio/access_token.rb
152
152
  - config/initializers/inflections.rb
153
+ - config/locales/de.yml
153
154
  - config/locales/en.yml
154
155
  - config/routes.rb
155
156
  - db/migrate/20190426155505_enable_postgres_extensions_for_uuids.rb
@@ -162,13 +163,15 @@ files:
162
163
  - lib/zaikio/oauth_client/client_configuration.rb
163
164
  - lib/zaikio/oauth_client/configuration.rb
164
165
  - lib/zaikio/oauth_client/engine.rb
166
+ - lib/zaikio/oauth_client/error.rb
167
+ - lib/zaikio/oauth_client/system_test_helper.rb
165
168
  - lib/zaikio/oauth_client/test_helper.rb
166
169
  - lib/zaikio/oauth_client/version.rb
167
170
  homepage: https://github.com/zaikio/zaikio-oauth_client
168
171
  licenses:
169
172
  - MIT
170
173
  metadata:
171
- changelog_uri: https://github.com/zaikio/zaikio-oauth_client/blob/master/CHANGELOG.md
174
+ changelog_uri: https://github.com/zaikio/zaikio-oauth_client/blob/main/CHANGELOG.md
172
175
  post_install_message:
173
176
  rdoc_options: []
174
177
  require_paths: