zaikio-oauth_client 0.6.0 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 70305a5016ca591d7275d6bf09627865e32417481047c3717099108c251a5d93
4
- data.tar.gz: 8274f9034a133d510be6fc6c42e6fb7c96e807fbac4b076ce0f42f13da7b2536
3
+ metadata.gz: 72f0e0de7e7d6ffc5b63c74cc85d4a6891d7e938273b1cf661a7fe52d4ac3068
4
+ data.tar.gz: 3999e67bb374120c7f2fd08c7bb25414a45b616f7386b417f85164a4e65251ef
5
5
  SHA512:
6
- metadata.gz: 9cae8d8c1dee2d253d38764adcd245ed042017c99bc70255db2f236e11d2c77ef20b75f366a30ca59eda1c1ab9673e54b6e7117b5ab2159d78a6afbc38c214fe
7
- data.tar.gz: 8cfd8a8df57d6076292ddca2253cb3cd9f79c2411fcadddc265757b5fea67c4dfabb67cd4406459e894708cee9b9d37bf6025cbb3fd455ceb575521cbb3000de
6
+ metadata.gz: 8196826c87334d5b762671c5fb723229f879da4c462deba8d1459754036d0328cf14f9fb8cbf10e8e67a55bf79dc9b9065523aa12ae46924eed574067d318270
7
+ data.tar.gz: a25db277b09d543b3f7ecee82a2cfab5b16d4163c2e5be80dc3881935c87622f3e458a02f05ef243adf8db9202d1651eda2267d9531a441006bd8151f284fb45
data/README.md CHANGED
@@ -103,7 +103,20 @@ redirect_to zaikio_oauth_client.new_session_path(client_name: 'my_other_client')
103
103
  redirect_to zaikio_oauth_client.new_connection_path(client_name: 'my_other_client')
104
104
  ```
105
105
 
106
- This will redirect the user to the OAuth Authorize endpoint of the Zaikio Directory `.../oauth/authorize` and include all necessary parameters like your client_id.
106
+ This will redirect the user to the OAuth Authorize endpoint of the Zaikio Directory
107
+ `.../oauth/authorize` and include all necessary parameters like your client_id. You may
108
+ also pass `show_signup`, `force_login` and `state` parameters through, like so:
109
+
110
+ ```ruby
111
+ # Take the user directly to the signup page
112
+ redirect_to zaikio_oauth_client.new_session_path(show_signup: true)
113
+
114
+ # Force the user to re-authenticate even if they have an existing session
115
+ redirect_to zaikio_oauth_client.new_session_path(force_login: true)
116
+
117
+ # Pass a custom Oauth 2.0 state parameter
118
+ redirect_to zaikio_oauth_client.new_session_path(state: "something-my-app-uses")
119
+ ```
107
120
 
108
121
  #### Session handling
109
122
 
@@ -5,8 +5,9 @@ module Zaikio
5
5
  class AccessToken < ApplicationRecord
6
6
  self.table_name = "zaikio_access_tokens"
7
7
 
8
- def self.build_from_access_token(access_token) # rubocop:disable Metrics/AbcSize
8
+ def self.build_from_access_token(access_token, requested_scopes: nil) # rubocop:disable Metrics/AbcSize
9
9
  payload = JWT.decode(access_token.token, nil, false).first rescue {} # rubocop:disable Style/RescueModifier
10
+ scopes = access_token.params["scope"].split(",")
10
11
  new(
11
12
  id: payload["jti"],
12
13
  bearer_type: access_token.params["bearer"]["type"],
@@ -15,7 +16,8 @@ module Zaikio
15
16
  token: access_token.token,
16
17
  refresh_token: access_token.refresh_token,
17
18
  expires_at: Time.strptime(access_token.expires_at.to_s, "%s"),
18
- scopes: access_token.params["scope"].split(",")
19
+ scopes: scopes,
20
+ requested_scopes: requested_scopes || scopes
19
21
  )
20
22
  end
21
23
 
@@ -38,9 +40,9 @@ module Zaikio
38
40
  .where("refresh_token IS NOT NULL")
39
41
  .where.not(id: Zaikio::JWTAuth.revoked_token_ids)
40
42
  }
41
- scope :by_bearer, lambda { |bearer_id:, scopes: [], bearer_type: "Person"|
43
+ scope :by_bearer, lambda { |bearer_id:, requested_scopes: [], bearer_type: "Person"|
42
44
  where(bearer_type: bearer_type, bearer_id: bearer_id)
43
- .where("scopes @> ARRAY[?]::varchar[]", scopes)
45
+ .where("requested_scopes @> ARRAY[?]::varchar[]", requested_scopes)
44
46
  }
45
47
  scope :usable, lambda { |options|
46
48
  by_bearer(**options).valid.or(by_bearer(**options).valid_refresh)
@@ -60,7 +62,7 @@ module Zaikio
60
62
  end
61
63
 
62
64
  def bearer_klass
63
- return unless Zaikio.const_defined?("Hub::Models")
65
+ return unless Zaikio.const_defined?("Hub::Models", false)
64
66
 
65
67
  if Zaikio::Hub::Models.configuration.respond_to?(:"#{bearer_type.underscore}_class_name")
66
68
  Zaikio::Hub::Models.configuration.public_send(:"#{bearer_type.underscore}_class_name").constantize
@@ -76,12 +78,17 @@ module Zaikio
76
78
  attributes.slice("token", "refresh_token")
77
79
  ).refresh!
78
80
 
79
- access_token = self.class.build_from_access_token(refreshed_token)
81
+ destroy
80
82
 
81
- transaction { destroy if access_token.save! }
82
-
83
- access_token
83
+ self.class.build_from_access_token(
84
+ refreshed_token,
85
+ requested_scopes: requested_scopes
86
+ ).tap(&:save!)
84
87
  end
88
+ rescue OAuth2::Error => e
89
+ raise unless e.code == "invalid_grant"
90
+
91
+ nil
85
92
  end
86
93
  end
87
94
  end
@@ -0,0 +1,6 @@
1
+ class AddRequestedScopesToZaikioAccessTokens < ActiveRecord::Migration[6.1]
2
+ def change
3
+ add_column :zaikio_access_tokens, :requested_scopes, :string, array: true, default: [], null: false
4
+ Zaikio::AccessToken.update_all("requested_scopes = scopes, updated_at = now()")
5
+ end
6
+ end
@@ -57,29 +57,50 @@ module Zaikio
57
57
  end
58
58
  end
59
59
 
60
- def get_access_token(client_name: nil, bearer_type: "Person", bearer_id: nil, scopes: nil) # rubocop:disable Metrics/MethodLength
61
- client_name ||= self.client_name
62
- client_config = client_config_for(client_name)
60
+ # Finds the best possible access token, using the DB or an API call
61
+ # * If the token has expired, it will be refreshed using the refresh_token flow
62
+ # (if this fails, we fallback to getting a new token using client_credentials)
63
+ # * If the token does not exist, we'll get a new one using the client_credentials flow
64
+ def get_access_token(bearer_id:, client_name: nil, bearer_type: "Person", scopes: nil)
65
+ client_config = client_config_for(client_name || self.client_name)
63
66
  scopes ||= client_config.default_scopes_for(bearer_type)
64
67
 
65
- access_token = Zaikio::AccessToken.where(audience: client_config.client_name)
66
- .usable(bearer_type: bearer_type, bearer_id: bearer_id, scopes: scopes)
67
- .first
68
-
69
- if access_token.blank?
70
- access_token = Zaikio::AccessToken.build_from_access_token(
71
- client_config.token_by_client_credentials(
72
- bearer_type: bearer_type,
73
- bearer_id: bearer_id,
74
- scopes: scopes
75
- )
68
+ token = find_usable_access_token(client_name: client_config.client_name,
69
+ bearer_type: bearer_type,
70
+ bearer_id: bearer_id,
71
+ requested_scopes: scopes)
72
+
73
+ token = token.refresh! if token&.expired?
74
+
75
+ token ||= fetch_new_token(client_config: client_config,
76
+ bearer_type: bearer_type,
77
+ bearer_id: bearer_id,
78
+ scopes: scopes)
79
+ token
80
+ end
81
+
82
+ # Finds the best usable access token. Note that this token may have expired and
83
+ # would require refreshing.
84
+ 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
76
91
  )
77
- access_token.save!
78
- elsif access_token&.expired?
79
- access_token = access_token.refresh!
80
- end
92
+ .first
93
+ end
81
94
 
82
- access_token
95
+ def fetch_new_token(client_config:, bearer_type:, bearer_id:, scopes:)
96
+ Zaikio::AccessToken.build_from_access_token(
97
+ client_config.token_by_client_credentials(
98
+ bearer_type: bearer_type,
99
+ bearer_id: bearer_id,
100
+ scopes: scopes
101
+ ),
102
+ requested_scopes: scopes
103
+ ).tap(&:save!)
83
104
  end
84
105
 
85
106
  def get_plain_scopes(scopes)
@@ -4,11 +4,13 @@ module Zaikio
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  def new
7
- cookies.encrypted[:origin] = params[:origin]
7
+ opts = params.permit(:client_name, :show_signup, :force_login, :state)
8
+ client_name = opts.delete(:client_name)
8
9
 
9
10
  redirect_to oauth_client.auth_code.authorize_url(
10
- redirect_uri: approve_url(params[:client_name]),
11
- scope: oauth_scope
11
+ redirect_uri: approve_url(client_name),
12
+ scope: oauth_scope,
13
+ **opts
12
14
  )
13
15
  end
14
16
 
@@ -49,10 +51,10 @@ module Zaikio
49
51
  def create_access_token
50
52
  access_token_response = oauth_client.auth_code.get_token(params[:code])
51
53
 
52
- access_token = Zaikio::AccessToken.build_from_access_token(access_token_response)
53
- access_token.save!
54
-
55
- access_token
54
+ Zaikio::AccessToken.build_from_access_token(
55
+ access_token_response,
56
+ requested_scopes: client_config.default_scopes
57
+ ).tap(&:save!)
56
58
  end
57
59
 
58
60
  def client_name
@@ -1,5 +1,5 @@
1
1
  module Zaikio
2
2
  module OAuthClient
3
- VERSION = "0.6.0".freeze
3
+ VERSION = "0.8.0".freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,17 +1,59 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zaikio-oauth_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.8.0
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-02-23 00:00:00.000000000 Z
11
+ date: 2021-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rails
14
+ name: actionpack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 5.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 5.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 5.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 5.0.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 5.0.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: railties
15
57
  requirement: !ruby/object:Gem::Requirement
16
58
  requirements:
17
59
  - - ">="
@@ -112,6 +154,7 @@ files:
112
154
  - db/migrate/20190426155505_enable_postgres_extensions_for_uuids.rb
113
155
  - db/migrate/20191017132048_create_zaikio_access_tokens.rb
114
156
  - db/migrate/20210222135920_enhance_access_token_index.rb
157
+ - db/migrate/20210224154303_add_requested_scopes_to_zaikio_access_tokens.rb
115
158
  - lib/tasks/zaikio_tasks.rake
116
159
  - lib/zaikio/oauth_client.rb
117
160
  - lib/zaikio/oauth_client/authenticatable.rb