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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d4a3c825a4085266a663c307db837c9daf4c12c002c0dc8a31daddfab67993ef
|
4
|
+
data.tar.gz: b342bd603a6a1ab474bea589babfb232b0aeb49486e9df1418151c6675a291e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
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
|
-
|
154
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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:
|
11
|
+
date: 2022-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|