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

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: 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