zaikio-oauth_client 0.6.0 → 0.8.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: 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