atlassian-jwt-authentication 0.9.0.pre8 → 0.9.1.pre9

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: 4b613184861f6210fe60369212d95288f3bc85a71a9906714c71d966e1606381
4
- data.tar.gz: 320921baeac4b8bdfba96131fcb0a05077242f41e96b7ee3d9e3b03ae5dace01
3
+ metadata.gz: d4a3c825a4085266a663c307db837c9daf4c12c002c0dc8a31daddfab67993ef
4
+ data.tar.gz: b342bd603a6a1ab474bea589babfb232b0aeb49486e9df1418151c6675a291e2
5
5
  SHA512:
6
- metadata.gz: 026ca8a88864fdfa13db84a94d403291ee10935bdcc99a31366a5f93d467479f21c308789f3e06086d206ab4e9656e992431de705095baefbf347d954549717a
7
- data.tar.gz: a91c87fe92112b9bef084198e2d2b6aecf82f64e3c881de0448dab04cb463be430993e8a91ead79656f42f97350bdb620778eb8d73c299027bec88f48b7a25e7
6
+ metadata.gz: f4440680e3c850ec815176ec2d6f29c88d466e53c72a22f85aefb7f0ee86c85505477a84562232ad72fceaf557f46c67a72aefeeeed9f3a2ad0e7e013c195c86
7
+ data.tar.gz: 496ed5d17f08b4577db4b74a6dac5473807ee250378509a431fd891e0818e26ddc4da3a05f99d3689980105f2efd51aa0b96731cbe5618629ae9a420de7a3a4c
data/README.md CHANGED
@@ -256,6 +256,12 @@ Config | Environment variable | Description | Default |
256
256
  `AtlassianJwtAuthentication.debug_requests` | `AJA_DEBUG_REQUESTS` | when `true` HTTP requests will include body content, implicitly turns on `log_requests` | `false`
257
257
  `AtlassianJwtAuthentication.signed_install` | `AJA_SIGNED_INSTALL` | Installation lifecycle security improvements. Migration process described [here](https://community.developer.atlassian.com/t/action-required-atlassian-connect-installation-lifecycle-security-improvements/49046). In the descriptor set `"apiMigrations":{"signed-install":AtlassianJwtAuthentication.signed_install}` | `false`
258
258
 
259
+ ### Middleware configuration
260
+
261
+ Parameter | Description /
262
+ --------- |-------------|
263
+ `force_asymmetric_verify` | A `proc` expected to return 'true' if the currently processed request must be validated with RS256 algorithm. Used for _signed_install_ endpoints.
264
+
259
265
  ## Requirements
260
266
 
261
267
  Ruby 2.0+, ActiveRecord 4.1+
@@ -24,17 +24,17 @@ module AtlassianJwtAuthentication
24
24
  base_url = params[:baseUrl]
25
25
  api_base_url = params[:baseApiUrl] || base_url
26
26
 
27
+ # All install (including upgrades) / uninstall hooks are asymmetrically
28
+ # signed with RS256 (RSA Signature with SHA-256) algorithm
29
+ return false unless _verify_jwt(addon_key, force_asymmetric_verify: true)
30
+
27
31
  jwt_auth = JwtToken.where(client_key: client_key, addon_key: addon_key).first
28
- if jwt_auth
29
- # The add-on was previously installed on this client
30
- return false unless _verify_jwt(addon_key)
31
- if jwt_auth.id != current_jwt_token.id
32
- # Update request was issued to another plugin
33
- render_forbidden
34
- return false
35
- end
36
- else
32
+ if jwt_auth.nil?
37
33
  self.current_jwt_token = JwtToken.new(jwt_token_params)
34
+ elsif jwt_auth.id != current_jwt_token.id
35
+ # Update request was issued to another plugin
36
+ render_forbidden
37
+ return false
38
38
  end
39
39
 
40
40
  current_jwt_token.addon_key = addon_key
@@ -55,7 +55,7 @@ module AtlassianJwtAuthentication
55
55
  def on_add_on_uninstalled
56
56
  addon_key = params[:key]
57
57
 
58
- return unless _verify_jwt(addon_key)
58
+ return unless _verify_jwt(addon_key, force_asymmetric_verify: true)
59
59
 
60
60
  client_key = params[:clientKey]
61
61
 
@@ -67,7 +67,7 @@ module AtlassianJwtAuthentication
67
67
  end
68
68
 
69
69
  def verify_jwt(addon_key, skip_qsh_verification: false)
70
- _verify_jwt(addon_key, true, skip_qsh_verification: skip_qsh_verification)
70
+ _verify_jwt(addon_key, consider_param: true, skip_qsh_verification: skip_qsh_verification)
71
71
  end
72
72
 
73
73
  def ensure_license
@@ -93,7 +93,7 @@ module AtlassianJwtAuthentication
93
93
  end
94
94
 
95
95
  unless response.data['state'] == 'ENABLED' &&
96
- response.data['license'] && response.data['license']['active']
96
+ response.data['license'] && response.data['license']['active']
97
97
  log(:error, "client #{current_jwt_token.client_key}: no active & enabled license was found")
98
98
  render_payment_required
99
99
  return false
@@ -107,7 +107,7 @@ module AtlassianJwtAuthentication
107
107
 
108
108
  private
109
109
 
110
- def _verify_jwt(addon_key, consider_param = false, skip_qsh_verification: false)
110
+ def _verify_jwt(addon_key, consider_param: false, skip_qsh_verification: false, force_asymmetric_verify: false)
111
111
  self.current_jwt_token = nil
112
112
  self.current_account_id = nil
113
113
  self.current_jwt_context = nil
@@ -130,7 +130,7 @@ module AtlassianJwtAuthentication
130
130
  jwt = possible_jwt if algorithm == 'JWT'
131
131
  end
132
132
 
133
- jwt_verification = AtlassianJwtAuthentication::JWTVerification.new(addon_key, nil, jwt, request)
133
+ jwt_verification = AtlassianJwtAuthentication::JWTVerification.new(addon_key, nil, force_asymmetric_verify, jwt, request)
134
134
  jwt_verification.exclude_qsh_params = exclude_qsh_params
135
135
  jwt_verification.logger = logger if defined?(logger)
136
136
 
@@ -150,8 +150,8 @@ module AtlassianJwtAuthentication
150
150
 
151
151
  def jwt_token_params
152
152
  {
153
- client_key: params.permit(:clientKey)['clientKey'],
154
- addon_key: params.permit(:key)['key']
153
+ client_key: params.permit(:clientKey)['clientKey'],
154
+ addon_key: params.permit(:key)['key']
155
155
  }
156
156
  end
157
157
 
@@ -13,6 +13,7 @@ module AtlassianJwtAuthentication
13
13
  @addon_key = options[:addon_key]
14
14
  @if = options[:if]
15
15
  @audience = options[:audience]
16
+ @force_asymmetric_verify = options[:force_asymmetric_verify]
16
17
  end
17
18
 
18
19
  def call(env)
@@ -31,7 +32,9 @@ module AtlassianJwtAuthentication
31
32
  end
32
33
 
33
34
  if jwt
34
- jwt_verification = JWTVerification.new(@addon_key, @audience, jwt, request)
35
+ force_asymmetric_verify = @force_asymmetric_verify && @force_asymmetric_verify.call(env)
36
+
37
+ jwt_verification = JWTVerification.new(@addon_key, @audience, force_asymmetric_verify, jwt, request)
35
38
  jwt_auth, account_id, context, qsh_verified = jwt_verification.verify
36
39
 
37
40
  if jwt_auth
@@ -2,11 +2,12 @@ require 'jwt'
2
2
 
3
3
  module AtlassianJwtAuthentication
4
4
  class JWTVerification
5
- attr_accessor :addon_key, :jwt, :audience, :request, :exclude_qsh_params, :logger
5
+ attr_accessor :addon_key, :jwt, :audience, :force_asymmetric_verify, :request, :exclude_qsh_params, :logger
6
6
 
7
- def initialize(addon_key, audience, jwt, request, &block)
7
+ def initialize(addon_key, audience, force_asymmetric_verify, jwt, request, &block)
8
8
  self.addon_key = addon_key
9
9
  self.audience = audience
10
+ self.force_asymmetric_verify = force_asymmetric_verify
10
11
  self.jwt = jwt
11
12
  self.request = request
12
13
 
@@ -39,18 +40,14 @@ module AtlassianJwtAuthentication
39
40
  addon_key: addon_key
40
41
  ).first
41
42
 
42
- unless jwt_auth
43
- log(:error, "Could not find jwt_token for client_key #{data['iss']} and addon_key #{addon_key}")
44
- return false
45
- end
46
-
47
43
  # Discard the tokens without verification
48
44
  if encoding_data['alg'] == 'none'
49
45
  log(:error, "The JWT checking algorithm was set to none for client_key #{data['iss']} and addon_key #{addon_key}")
50
46
  return false
51
47
  end
52
48
 
53
- if AtlassianJwtAuthentication.signed_install && encoding_data['alg'] == 'RS256'
49
+ if force_asymmetric_verify ||
50
+ AtlassianJwtAuthentication.signed_install && encoding_data['alg'] == 'RS256'
54
51
  response = Faraday.get("https://connect-install-keys.atlassian.com/#{encoding_data['kid']}")
55
52
  unless response.success? && response.body
56
53
  log(:error, "Error retrieving atlassian public key. Response code #{response.status} and kid #{encoding_data['kid']}")
@@ -58,15 +55,24 @@ module AtlassianJwtAuthentication
58
55
  end
59
56
 
60
57
  decode_key = OpenSSL::PKey::RSA.new(response.body)
61
- decode_options = {algorithms: ['RS256'], verify_aud: true, aud: audience}
62
58
  else
59
+ unless jwt_auth
60
+ log(:error, "Could not find jwt_token for client_key #{data['iss']} and addon_key #{addon_key}")
61
+ return false
62
+ end
63
+
63
64
  decode_key = jwt_auth.shared_secret
64
- decode_options = {}
65
+ end
66
+
67
+ decode_options = {}
68
+ if encoding_data['alg'] == 'RS256'
69
+ decode_options = { algorithms: ['RS256'] }
65
70
  end
66
71
 
67
72
  # Decode the token again, this time with signature & claims verification
68
73
  options = JWT::DefaultOptions::DEFAULT_OPTIONS.merge(verify_expiration: AtlassianJwtAuthentication.verify_jwt_expiration).merge(decode_options)
69
74
  decoder = JWT::Decode.new(jwt, decode_key, true, options)
75
+
70
76
  begin
71
77
  payload, header = decoder.decode_segments
72
78
  rescue JWT::VerificationError
@@ -85,7 +91,7 @@ module AtlassianJwtAuthentication
85
91
  if data['qsh']
86
92
  # Verify the query has not been tampered by Creating a Query Hash and
87
93
  # comparing it against the qsh claim on the verified token
88
- if jwt_auth.base_url.present? && request.url.include?(jwt_auth.base_url)
94
+ if jwt_auth.present? && jwt_auth.base_url.present? && request.url.include?(jwt_auth.base_url)
89
95
  path = request.url.gsub(jwt_auth.base_url, '')
90
96
  else
91
97
  path = request.path.gsub(AtlassianJwtAuthentication::context_path, '')
@@ -1,7 +1,7 @@
1
1
  module AtlassianJwtAuthentication
2
2
  MAJOR_VERSION = "0"
3
3
  MINOR_VERSION = "9"
4
- PATH_VERSION = "0"
4
+ PATCH_VERSION = "1"
5
5
 
6
6
  # rubygems don't support semantic versioning - https://github.com/rubygems/rubygems/issues/592, using GITHUB_RUN_NUMBER to represent build number
7
7
  # going to release pre versions automatically
@@ -12,7 +12,7 @@ module AtlassianJwtAuthentication
12
12
  [
13
13
  MAJOR_VERSION,
14
14
  MINOR_VERSION,
15
- PATH_VERSION
15
+ PATCH_VERSION
16
16
  ].join(".") + BUILD_NUMBER
17
17
  ).freeze
18
18
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atlassian-jwt-authentication
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0.pre8
4
+ version: 0.9.1.pre9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Laura Barladeanu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-16 00:00:00.000000000 Z
11
+ date: 2022-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable