zaikio-oauth_client 0.17.2 → 0.19.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: 605b297bfe708d26eb51ed1060bf243fc3060fd27df8f4ec481dcf94a471fcc2
4
- data.tar.gz: 839bb62b7d00b272978fa3f225bd7416a7c12cf7508e4ab2242183fcb7c8d801
3
+ metadata.gz: d82ba1e01192f3e9fac8f47bc5ded67fb2b869f724fcdcd26ec42718c4ca53f5
4
+ data.tar.gz: 48a24101ed54396c68d96077c0eaf7dc16fbd317ecbae092773ae9f29a31b9bd
5
5
  SHA512:
6
- metadata.gz: 2c7e04798b1ca7338e30005794e2d804a90633816323f678bd2f9a7bcb7f9b8f368e3e68ac38490c44bf48761db785169f41e8f9cf22ce7cdee7b40b82e5ee05
7
- data.tar.gz: 189afa394a1a0739d5a4ed60ed4fc667ee8aed698329fb7eb87cc6785bfc88ee610028d9b5fb4ac8ba389c399104e8956c8656ce0a4bbb1132b039f6b1612446
6
+ metadata.gz: 46884a09302f56b53e64e8ee2cd1172c5792a1ecf82ff6e0a8b054d979f86f3b63449a4bd79633c3f95f076e11e0f3bc5669b26d8988643232c784c92f447d5b
7
+ data.tar.gz: f1e5992edef1cda3bd6b68f2089c07201264d2b1e64a4713ae0d6ec84af5e708172b9b53ceb76d38245645ec41ebf79f4da7d63a40e17a6441e4d7171f9e47e8
data/README.md CHANGED
@@ -14,7 +14,17 @@ Then run `bundle install`.
14
14
 
15
15
  ## Setup & Configuration
16
16
 
17
- ### 1. Copy & run Migrations
17
+ ### 1. Setup Active Record encryption
18
+
19
+ Setup [Active Record Encryption](https://guides.rubyonrails.org/active_record_encryption.html#setup) by running:
20
+
21
+ ```
22
+ rails db:encryption:init
23
+ ```
24
+
25
+ (Continue generating the credentials each for different environments)
26
+
27
+ ### 2. Copy & run Migrations
18
28
 
19
29
  ```bash
20
30
  rails zaikio_oauth_client:install:migrations
@@ -24,7 +34,7 @@ rails db:migrate
24
34
  This will create the tables:
25
35
  + `zaikio_access_tokens`
26
36
 
27
- ### 2. Mount routes
37
+ ### 3. Mount routes
28
38
 
29
39
  Add this to `config/routes.rb`:
30
40
 
@@ -32,7 +42,7 @@ Add this to `config/routes.rb`:
32
42
  mount Zaikio::OAuthClient::Engine => "/zaikio"
33
43
  ```
34
44
 
35
- ### 3. Configure Gem
45
+ ### 4. Configure Gem
36
46
 
37
47
  ```rb
38
48
  # config/initializers/zaikio_oauth_client.rb
@@ -70,7 +80,7 @@ end
70
80
  ```
71
81
 
72
82
 
73
- ### 4. Clean up outdated access tokens (recommended)
83
+ ### 5. Clean up outdated access tokens (recommended)
74
84
 
75
85
  To avoid keeping all expired oath and refresh tokens in your database, we recommend to implement their scheduled deletion. We recommend therefore to use a schedule gems such as [sidekiq](https://github.com/mperham/sidekiq) and [sidekiq-scheduler](https://github.com/moove-it/sidekiq-scheduler).
76
86
 
@@ -135,12 +145,19 @@ redirect_to zaikio_oauth_client.new_subscription_path(plan: "free")
135
145
 
136
146
  #### Session handling
137
147
 
138
- The Zaikio gem engine will set a cookie for the user after a successful OAuth flow: `session[:zaikio_person_id]`.
148
+ The Zaikio gem engine will set a cookie for the access token after a successful OAuth flow: `session[:zaikio_access_token_id]`.
139
149
 
140
150
  If you are using for example `Zaikio::Hub::Models`, you can use this snippet to set the current user:
141
151
 
142
152
  ```ruby
143
- Current.user ||= Zaikio::Hub::Models::Person.find_by(id: session[:zaikio_person_id])
153
+ access_token = Zaikio::OAuthClient.find_active_access_token(session[:zaikio_access_token_id])
154
+ session[:zaikio_access_token_id] = access_token&.id
155
+ Current.user = Zaikio::Hub::Models::Person.find_by(id: access_token&.bearer_id)
156
+
157
+ unless Current.user
158
+ session[:origin] = request.fullpath
159
+ redirect_to zaikio_oauth_client.new_session_path
160
+ end
144
161
  ````
145
162
 
146
163
  You can then use `Current.user` anywhere.
@@ -5,7 +5,11 @@ 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, requested_scopes: nil)
8
+ # Encryption
9
+ encrypts :token
10
+ encrypts :refresh_token
11
+
12
+ def self.build_from_access_token(access_token, requested_scopes: nil, include_refresh_token: true)
9
13
  payload = JWT.decode(access_token.token, nil, false).first rescue {} # rubocop:disable Style/RescueModifier
10
14
  scopes = access_token.params["scope"].split(",")
11
15
  new(
@@ -14,7 +18,7 @@ module Zaikio
14
18
  bearer_id: access_token.params["bearer"]["id"],
15
19
  audience: access_token.params["audiences"].first,
16
20
  token: access_token.token,
17
- refresh_token: access_token.refresh_token,
21
+ refresh_token: (access_token.refresh_token if include_refresh_token),
18
22
  expires_at: Time.strptime(access_token.expires_at.to_s, "%s"),
19
23
  scopes: scopes,
20
24
  requested_scopes: requested_scopes || scopes
@@ -63,7 +67,7 @@ module Zaikio
63
67
  end
64
68
 
65
69
  def bearer_klass
66
- return unless Zaikio.const_defined?("Hub::Models", false) # rubocop:disable Performance/StringIdentifierArgument
70
+ return unless Zaikio.const_defined?("Hub::Models", false)
67
71
 
68
72
  if Zaikio::Hub::Models.configuration.respond_to?(:"#{bearer_type.underscore}_class_name")
69
73
  Zaikio::Hub::Models.configuration.public_send(:"#{bearer_type.underscore}_class_name").constantize
@@ -91,5 +95,15 @@ module Zaikio
91
95
  destroy
92
96
  nil
93
97
  end
98
+
99
+ def revoke!
100
+ return unless Zaikio.const_defined?("Hub::RevokedAccessToken", false)
101
+
102
+ Zaikio::Hub.with_token(token) do
103
+ Zaikio::Hub::RevokedAccessToken.create
104
+ end
105
+ rescue Zaikio::ConnectionError => e
106
+ Zaikio::OAuthClient.configuration.logger.warn "Access Token #{id} could not be revoked: #{e.message}"
107
+ end
94
108
  end
95
109
  end
@@ -0,0 +1,45 @@
1
+ class EncryptTokens < ActiveRecord::Migration[7.0]
2
+ def change
3
+ reversible do |dir|
4
+ dir.up do
5
+ rename_column :zaikio_access_tokens, :token, :unencrypted_token
6
+ rename_column :zaikio_access_tokens, :refresh_token, :unencrypted_refresh_token
7
+
8
+ add_column :zaikio_access_tokens, :token, :string
9
+ add_column :zaikio_access_tokens, :refresh_token, :string
10
+
11
+ Zaikio::AccessToken.find_each do |access_token|
12
+ access_token.update(
13
+ token: access_token.unencrypted_token,
14
+ refresh_token: access_token.unencrypted_refresh_token
15
+ )
16
+ end
17
+
18
+ change_column_null :zaikio_access_tokens, :token, false
19
+
20
+ remove_column :zaikio_access_tokens, :unencrypted_token, :string
21
+ remove_column :zaikio_access_tokens, :unencrypted_refresh_token, :string
22
+ end
23
+
24
+ dir.down do
25
+ add_column :zaikio_access_tokens, :unencrypted_token, :string
26
+ add_column :zaikio_access_tokens, :unencrypted_refresh_token, :string
27
+
28
+ Zaikio::AccessToken.find_each do |access_token|
29
+ access_token.update_columns(
30
+ unencrypted_token: access_token.token,
31
+ unencrypted_refresh_token: access_token.refresh_token
32
+ )
33
+ end
34
+
35
+ remove_column :zaikio_access_tokens, :token, :string
36
+ remove_column :zaikio_access_tokens, :refresh_token, :string
37
+
38
+ rename_column :zaikio_access_tokens, :unencrypted_token, :token
39
+ rename_column :zaikio_access_tokens, :unencrypted_refresh_token, :refresh_token
40
+
41
+ change_column_null :zaikio_access_tokens, :token, false
42
+ end
43
+ end
44
+ end
45
+ end
@@ -55,13 +55,16 @@ module Zaikio
55
55
  end
56
56
 
57
57
  def destroy
58
- access_token_id = session[:zaikio_access_token_id]
58
+ if (access_token = Zaikio::AccessToken.valid.or(Zaikio::AccessToken.valid_refresh)
59
+ .find_by(id: session[:zaikio_access_token_id]))
60
+ access_token.revoke!
61
+ end
59
62
  session.delete(:zaikio_access_token_id)
60
63
  session.delete(:origin)
61
64
 
62
65
  redirect_to send(
63
66
  respond_to?(:after_destroy_path_for) ? :after_destroy_path_for : :default_after_destroy_path_for,
64
- access_token_id
67
+ access_token.id
65
68
  )
66
69
  end
67
70
 
@@ -1,5 +1,5 @@
1
1
  module Zaikio
2
2
  module OAuthClient
3
- VERSION = "0.17.2".freeze
3
+ VERSION = "0.19.0".freeze
4
4
  end
5
5
  end
@@ -6,7 +6,7 @@ require "zaikio/oauth_client/configuration"
6
6
  require "zaikio/oauth_client/authenticatable"
7
7
 
8
8
  module Zaikio
9
- module OAuthClient
9
+ module OAuthClient # rubocop:disable Metrics/ModuleLength
10
10
  class << self
11
11
  attr_reader :client_name
12
12
 
@@ -58,9 +58,8 @@ module Zaikio
58
58
  end
59
59
  end
60
60
 
61
- # Finds the best possible access token, using the DB or an API call
62
- # * If the token has expired, it will be refreshed using the refresh_token flow
63
- # (if this fails, we fallback to getting a new token using client_credentials)
61
+ # Finds active access token, using the DB or Client Credentials flow
62
+ # * It searches in the DB for an active access token
64
63
  # * If the token does not exist, we'll get a new one using the client_credentials flow
65
64
  def get_access_token(bearer_id:, client_name: nil, bearer_type: "Person", scopes: nil, valid_for: 30.seconds)
66
65
  client_config = client_config_for(client_name || self.client_name)
@@ -72,8 +71,6 @@ module Zaikio
72
71
  requested_scopes: scopes,
73
72
  valid_for: valid_for)
74
73
 
75
- token = token.refresh! if token&.expired?
76
-
77
74
  token ||= fetch_new_token(client_config: client_config,
78
75
  bearer_type: bearer_type,
79
76
  bearer_id: bearer_id,
@@ -81,21 +78,31 @@ module Zaikio
81
78
  token
82
79
  end
83
80
 
84
- # Finds the best usable access token. Note that this token may have expired and
85
- # would require refreshing.
86
- def find_usable_access_token(client_name:, bearer_type:, bearer_id:, requested_scopes:, valid_for: 30.seconds) # rubocop:disable Metrics/MethodLength
87
- configuration.logger.debug "Try to fetch token for client_name: #{client_name}, "\
81
+ # This method can be used to find an active access token by id.
82
+ # It might refresh the access token to get an active one.
83
+ def find_active_access_token(id)
84
+ return unless id
85
+
86
+ access_token = Zaikio::AccessToken.find_by(id: id)
87
+ access_token = access_token.refresh! if access_token&.expired?
88
+
89
+ access_token
90
+ end
91
+
92
+ # Finds active access token with matching criteria for bearer and scopes.
93
+ def find_usable_access_token(client_name:, bearer_type:, bearer_id:, requested_scopes:, valid_for: 30.seconds) # rubocop:disable Metrics/MethodLength
94
+ configuration.logger.debug "Try to fetch token for client_name: #{client_name}, " \
88
95
  "bearer #{bearer_type}/#{bearer_id}, requested_scopes: #{requested_scopes}"
89
96
 
90
97
  fetch_access_token = lambda {
91
98
  Zaikio::AccessToken
92
99
  .where(audience: client_name)
93
- .usable(
100
+ .by_bearer(
94
101
  bearer_type: bearer_type,
95
102
  bearer_id: bearer_id,
96
- requested_scopes: requested_scopes,
97
- valid_until: valid_for.from_now
103
+ requested_scopes: requested_scopes
98
104
  )
105
+ .valid(valid_for.from_now)
99
106
  .first
100
107
  }
101
108
 
@@ -113,7 +120,10 @@ module Zaikio
113
120
  bearer_id: bearer_id,
114
121
  scopes: scopes
115
122
  ),
116
- requested_scopes: scopes
123
+ requested_scopes: scopes,
124
+ include_refresh_token: false
125
+ # Do not store refresh token on client credentials flow
126
+ # https://docs.zaikio.com/changelog/2022-08-09_client-credentials-drop-refresh-token.html
117
127
  ).tap(&:save!)
118
128
  end
119
129
 
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.17.2
4
+ version: 0.19.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: 2022-01-07 00:00:00.000000000 Z
11
+ date: 2022-08-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -89,7 +89,7 @@ dependencies:
89
89
  version: '0.5'
90
90
  - - "<"
91
91
  - !ruby/object:Gem::Version
92
- version: '2.0'
92
+ version: '3.0'
93
93
  type: :runtime
94
94
  prerelease: false
95
95
  version_requirements: !ruby/object:Gem::Requirement
@@ -99,7 +99,7 @@ dependencies:
99
99
  version: '0.5'
100
100
  - - "<"
101
101
  - !ruby/object:Gem::Version
102
- version: '2.0'
102
+ version: '3.0'
103
103
  - !ruby/object:Gem::Dependency
104
104
  name: pg
105
105
  requirement: !ruby/object:Gem::Requirement
@@ -157,6 +157,7 @@ files:
157
157
  - db/migrate/20191017132048_create_zaikio_access_tokens.rb
158
158
  - db/migrate/20210222135920_enhance_access_token_index.rb
159
159
  - db/migrate/20210224154303_add_requested_scopes_to_zaikio_access_tokens.rb
160
+ - db/migrate/20220425130923_encrypt_tokens.rb
160
161
  - lib/tasks/zaikio_tasks.rake
161
162
  - lib/zaikio/oauth_client.rb
162
163
  - lib/zaikio/oauth_client/authenticatable.rb