atlassian-jwt 0.1.1 → 0.2.0
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 +5 -5
- data/LICENSE.txt +13 -0
- data/README.md +46 -41
- data/atlassian-jwt.gemspec +12 -12
- data/lib/atlassian/jwt.rb +16 -16
- data/lib/atlassian/jwt/version.rb +1 -1
- metadata +29 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e3eed187061bc95e7468c34ac29c49a245ecc58fd233561d44928a0b106904c6
|
4
|
+
data.tar.gz: b838ab4ff15309364c7dbbe7b47745c3366fc0d727ece760a9b99826f701af9e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c546539a198300fcf0b748a4041a685b6c868b053db449906d075fb1c052f2ee401302524dc043cbf9109c36222084e9d3a4eafd419e4a0df2b200ec3af44f9f
|
7
|
+
data.tar.gz: 71147aa45d2feed710f0840314a47c93a757f5957405386bb16149833175f855ee5bdbb6dfb541633694cd3581ff8d3e254f05f9e38327d7933f43a7e0728c24
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2013 Atlassian Pty Ltd.
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
# Atlassian::Jwt
|
2
2
|
|
3
|
+
In order to access the
|
4
|
+
[Atlassian Connect REST APIs](https://developer.atlassian.com/cloud/jira/platform/rest/)
|
5
|
+
an app authenticates using a JSON Web Token (JWT). The token is
|
6
|
+
generated using the app's secret key and contains a *claim* which
|
7
|
+
includes the app's key and a hashed version of the API URL the
|
8
|
+
app is accessing. This gem simplifies generating the claim.
|
9
|
+
|
3
10
|
This gem provides helpers for generating Atlassian specific JWT
|
4
|
-
claims. It also exposes the [ruby-jwt](https://github.com/jwt/ruby-jwt)
|
11
|
+
claims. It also exposes the [ruby-jwt](https://github.com/jwt/ruby-jwt)
|
5
12
|
gem's `encode` and `decode` methods.
|
6
13
|
|
7
|
-
[](https://bitbucket.org/atlassian/atlassian-jwt-ruby/addon/pipelines/home)
|
8
|
-
|
9
14
|
## Installation
|
10
15
|
|
11
16
|
Add this line to your application's Gemfile:
|
@@ -16,49 +21,46 @@ gem 'atlassian-jwt'
|
|
16
21
|
|
17
22
|
And then execute:
|
18
23
|
|
19
|
-
|
24
|
+
```sh
|
25
|
+
bundle
|
26
|
+
```
|
20
27
|
|
21
28
|
Or install it yourself as:
|
22
29
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
In order to access the
|
28
|
-
[Atlassian Connect REST APIs](https://developer.atlassian.com/static/connect/docs/latest/rest-apis/)
|
29
|
-
an add-on authenticates using a JSON Web Token (JWT). The token is
|
30
|
-
generated using the add-on's secret key and contains a *claim* which
|
31
|
-
includes the add-on's key and a hashed version of the API URL the
|
32
|
-
add-on is accessing. This gem simplifies generating the claim.
|
30
|
+
```sh
|
31
|
+
gem install atlassian-jwt
|
32
|
+
```
|
33
33
|
|
34
|
-
|
34
|
+
## Generating a JWT Token
|
35
35
|
|
36
36
|
```ruby
|
37
37
|
require 'atlassian/jwt'
|
38
38
|
|
39
39
|
# The URL of the API call, must include the query string, if any
|
40
40
|
url = 'https://jira.atlassian.com/rest/api/latest/issue/JRA-9'
|
41
|
-
# The key of the
|
41
|
+
# The key of the app as defined in the app description
|
42
42
|
issuer = 'com.atlassian.example'
|
43
|
-
|
44
|
-
|
43
|
+
# The HTTP Method (GET, POST, etc) of the API call
|
44
|
+
http_method = 'get'
|
45
|
+
# The shared secret returned when the app is installed
|
46
|
+
shared_secret = '...'
|
45
47
|
|
46
|
-
claim = Atlassian::Jwt.build_claims(issuer,url,http_method)
|
47
|
-
jwt = JWT.encode(claim,shared_secret)
|
48
|
+
claim = Atlassian::Jwt.build_claims(issuer, url, http_method)
|
49
|
+
jwt = JWT.encode(claim, shared_secret)
|
48
50
|
```
|
49
51
|
|
50
52
|
If the base URL of the API is not at the root of the site,
|
51
|
-
i.e.
|
52
|
-
in the base URL to
|
53
|
+
i.e. `https://site.atlassian.net/jira/rest/api`, you will need to pass
|
54
|
+
in the base URL to `build_claims`:
|
53
55
|
|
54
|
-
```
|
56
|
+
```ruby
|
55
57
|
url = 'https://site.atlassian.net/jira/rest/api/latest/issue/JRA-9'
|
56
58
|
base_url = 'https://site.atlassian.net'
|
57
59
|
|
58
60
|
claim = Atlassian::Jwt.build_claims(issuer, url, http_method, base_url)
|
59
61
|
```
|
60
62
|
|
61
|
-
The generated JWT can then be passed in an
|
63
|
+
The generated JWT can then be passed in an `Authorization` header or
|
62
64
|
in the query string:
|
63
65
|
|
64
66
|
```ruby
|
@@ -66,7 +68,7 @@ in the query string:
|
|
66
68
|
uri = URI('https://site.atlassian.net/rest/api/latest/issue/JRA-9')
|
67
69
|
http = Net::HTTP.new(uri.host, uri.port)
|
68
70
|
request = Net::HTTP::Get.new(uri.request_uri)
|
69
|
-
request.initialize_http_header({'
|
71
|
+
request.initialize_http_header({'Authorization' => "JWT #{jwt}"})
|
70
72
|
response = http.request(request)
|
71
73
|
```
|
72
74
|
|
@@ -87,15 +89,15 @@ claim = Atlassian::Jwt.build_claims(
|
|
87
89
|
url,
|
88
90
|
http_method,
|
89
91
|
base_url,
|
90
|
-
Time.now - 60.seconds
|
91
|
-
Time.now + 1.day
|
92
|
+
(Time.now - 60.seconds).to_i,
|
93
|
+
(Time.now + 1.day).to_i
|
92
94
|
)
|
93
95
|
```
|
94
96
|
|
95
|
-
|
97
|
+
## Decoding a JWT token
|
96
98
|
|
97
|
-
The JWT from the server is usually returned a param. The underlying
|
98
|
-
Ruby JWT gem returns an array with the first element being the
|
99
|
+
The JWT from the server is usually returned as a param. The underlying
|
100
|
+
Ruby JWT gem returns an array, with the first element being the claims
|
99
101
|
and the second being the JWT header, which contains information about
|
100
102
|
how the JWT was encoded.
|
101
103
|
|
@@ -104,9 +106,9 @@ claims, jwt_header = Atlassian::Jwt.decode(params[:jwt], shared_secret)
|
|
104
106
|
```
|
105
107
|
|
106
108
|
By default, the JWT gem verifies that the JWT is properly signed with
|
107
|
-
the shared secret and raises an error if it's not. However, sometimes
|
109
|
+
the shared secret and raises an error if it's not. However, sometimes it
|
108
110
|
is necessary to read the JWT first to determine which shared secret is
|
109
|
-
needed. In this case, use nil for the shared secret and follow it with
|
111
|
+
needed. In this case, use `nil` for the shared secret and follow it with
|
110
112
|
`false` to tell the gem to to verify the signature.
|
111
113
|
|
112
114
|
```ruby
|
@@ -118,17 +120,20 @@ details.
|
|
118
120
|
|
119
121
|
## Development
|
120
122
|
|
121
|
-
After checking out the repo, run `bin/setup` to install dependencies.
|
122
|
-
|
123
|
-
|
123
|
+
After checking out the repo, run `bin/setup` to install dependencies.
|
124
|
+
|
125
|
+
Run `rake spec` to run the tests.
|
126
|
+
|
127
|
+
Run `bin/console` for an interactive prompt that allows you to experiment.
|
128
|
+
|
129
|
+
Run `bundle exec rake install` to install this gem to your local machine.
|
124
130
|
|
125
|
-
To
|
126
|
-
|
127
|
-
|
128
|
-
version, push git commits and tags, and push the `.gem` file to
|
131
|
+
To release a new version, update the version number in `version.rb`, and
|
132
|
+
then run `bundle exec rake release`. It will create a git tag for the
|
133
|
+
version, push git commits and tags, and push the `.gem` file to
|
129
134
|
[rubygems.org](https://rubygems.org).
|
130
135
|
|
131
136
|
## Contributing
|
132
137
|
|
133
|
-
Bug reports and pull requests are welcome on Bitbucket at
|
134
|
-
https://bitbucket.org/atlassian/atlassian-jwt-ruby
|
138
|
+
Bug reports and pull requests are welcome on Bitbucket at:
|
139
|
+
https://bitbucket.org/atlassian/atlassian-jwt-ruby
|
data/atlassian-jwt.gemspec
CHANGED
@@ -4,24 +4,24 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'atlassian/jwt/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'atlassian-jwt'
|
8
8
|
spec.version = Atlassian::Jwt::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
9
|
+
spec.authors = ['Spike Ilacqua', 'Seb Ruiz', 'Ngoc Dao']
|
10
|
+
spec.email = ['spike@6kites.com', 'seb@sebruiz.net', 'ndao@atlassian.com']
|
11
11
|
|
12
12
|
spec.summary = %q{Encode and decode JWT tokens for use with the Atlassian Connect REST APIs.}
|
13
|
-
spec.description = %q{This gem simplifies generating the claims
|
14
|
-
spec.homepage =
|
13
|
+
spec.description = %q{This gem simplifies generating the claims needed to authenticate with the Atlassian Connect REST APIs.}
|
14
|
+
spec.homepage = 'https://bitbucket.org/atlassian/atlassian-jwt-ruby'
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
-
spec.bindir =
|
17
|
+
spec.bindir = 'exe'
|
18
18
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
-
spec.require_paths = [
|
19
|
+
spec.require_paths = ['lib']
|
20
20
|
|
21
|
-
spec.add_runtime_dependency 'jwt', '~> 1.
|
22
|
-
spec.add_development_dependency 'json', '~> 1.8'
|
21
|
+
spec.add_runtime_dependency 'jwt', '~> 2.1.0'
|
23
22
|
|
24
|
-
spec.add_development_dependency
|
25
|
-
|
26
|
-
spec.add_development_dependency
|
23
|
+
spec.add_development_dependency 'json', '~> 2.2.0'
|
24
|
+
|
25
|
+
spec.add_development_dependency 'rake', '~> 12.3.2'
|
26
|
+
spec.add_development_dependency 'rspec', '~> 3.8.0'
|
27
27
|
end
|
data/lib/atlassian/jwt.rb
CHANGED
@@ -9,13 +9,13 @@ module Atlassian
|
|
9
9
|
CANONICAL_QUERY_SEPARATOR = '&'
|
10
10
|
ESCAPED_CANONICAL_QUERY_SEPARATOR = '%26'
|
11
11
|
|
12
|
-
def decode(token,secret, validate = true, options = {})
|
13
|
-
options = {
|
14
|
-
::JWT.decode
|
12
|
+
def decode(token, secret, validate = true, options = {})
|
13
|
+
options = {:algorithm => 'HS256'}.merge(options)
|
14
|
+
::JWT.decode(token, secret, validate, options)
|
15
15
|
end
|
16
16
|
|
17
17
|
def encode(payload, secret, algorithm = 'HS256', header_fields = {})
|
18
|
-
::JWT.encode
|
18
|
+
::JWT.encode(payload, secret, algorithm, header_fields)
|
19
19
|
end
|
20
20
|
|
21
21
|
def create_query_string_hash(uri, http_method, base_uri)
|
@@ -28,19 +28,18 @@ module Atlassian
|
|
28
28
|
uri = URI.parse(uri) unless uri.kind_of? URI
|
29
29
|
base_uri = URI.parse(base_uri) unless base_uri.kind_of? URI
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
canonicalize_query_string(uri.query)
|
31
|
+
[
|
32
|
+
http_method.upcase,
|
33
|
+
canonicalize_uri(uri, base_uri),
|
34
|
+
canonicalize_query_string(uri.query)
|
36
35
|
].join(CANONICAL_QUERY_SEPARATOR)
|
37
36
|
end
|
38
37
|
|
39
|
-
def build_claims(issuer,url,http_method,base_url='',issued_at=nil,expires=nil,attributes={})
|
38
|
+
def build_claims(issuer, url, http_method, base_url = '', issued_at = nil, expires = nil, attributes = {})
|
40
39
|
issued_at ||= Time.now.to_i
|
41
40
|
expires ||= issued_at + 60
|
42
41
|
qsh = Digest::SHA256.hexdigest(
|
43
|
-
Atlassian::Jwt.create_canonical_request(url,http_method,base_url)
|
42
|
+
Atlassian::Jwt.create_canonical_request(url, http_method, base_url)
|
44
43
|
)
|
45
44
|
|
46
45
|
{
|
@@ -52,7 +51,7 @@ module Atlassian
|
|
52
51
|
end
|
53
52
|
|
54
53
|
def canonicalize_uri(uri, base_uri)
|
55
|
-
path = uri.path.sub(/^#{base_uri.path}/,'')
|
54
|
+
path = uri.path.sub(/^#{base_uri.path}/, '')
|
56
55
|
path = '/' if path.nil? || path.empty?
|
57
56
|
path = '/' + path unless path.start_with? '/'
|
58
57
|
path.chomp!('/') if path.length > 1
|
@@ -61,15 +60,16 @@ module Atlassian
|
|
61
60
|
|
62
61
|
def canonicalize_query_string(query)
|
63
62
|
return '' if query.nil? || query.empty?
|
63
|
+
|
64
64
|
query = CGI::parse(query)
|
65
65
|
query.delete('jwt')
|
66
66
|
query.each do |k, v|
|
67
|
-
query[k] = v.map {|a| CGI.escape a }.join(',') if v.is_a? Array
|
68
|
-
query[k].gsub!('+','%20')
|
69
|
-
query[k].gsub!('%7E','~')
|
67
|
+
query[k] = v.map { |a| CGI.escape a }.join(',') if v.is_a? Array
|
68
|
+
query[k].gsub!('+', '%20') # Use %20, not CGI.escape default of "+"
|
69
|
+
query[k].gsub!('%7E', '~') # Unescape "~" per JS tests
|
70
70
|
end
|
71
71
|
query = Hash[query.sort]
|
72
|
-
query.map {|k,v| "#{CGI.escape k}=#{v}" }.join(
|
72
|
+
query.map { |k,v| "#{CGI.escape k}=#{v}" }.join(CANONICAL_QUERY_SEPARATOR)
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
metadata
CHANGED
@@ -1,98 +1,87 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: atlassian-jwt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Spike Ilacqua
|
8
8
|
- Seb Ruiz
|
9
|
+
- Ngoc Dao
|
9
10
|
autorequire:
|
10
11
|
bindir: exe
|
11
12
|
cert_chain: []
|
12
|
-
date:
|
13
|
+
date: 2019-02-28 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: jwt
|
16
17
|
requirement: !ruby/object:Gem::Requirement
|
17
18
|
requirements:
|
18
|
-
- - ~>
|
19
|
+
- - "~>"
|
19
20
|
- !ruby/object:Gem::Version
|
20
|
-
version:
|
21
|
+
version: 2.1.0
|
21
22
|
type: :runtime
|
22
23
|
prerelease: false
|
23
24
|
version_requirements: !ruby/object:Gem::Requirement
|
24
25
|
requirements:
|
25
|
-
- - ~>
|
26
|
+
- - "~>"
|
26
27
|
- !ruby/object:Gem::Version
|
27
|
-
version:
|
28
|
+
version: 2.1.0
|
28
29
|
- !ruby/object:Gem::Dependency
|
29
30
|
name: json
|
30
31
|
requirement: !ruby/object:Gem::Requirement
|
31
32
|
requirements:
|
32
|
-
- - ~>
|
33
|
+
- - "~>"
|
33
34
|
- !ruby/object:Gem::Version
|
34
|
-
version:
|
35
|
+
version: 2.2.0
|
35
36
|
type: :development
|
36
37
|
prerelease: false
|
37
38
|
version_requirements: !ruby/object:Gem::Requirement
|
38
39
|
requirements:
|
39
|
-
- - ~>
|
40
|
+
- - "~>"
|
40
41
|
- !ruby/object:Gem::Version
|
41
|
-
version:
|
42
|
-
- !ruby/object:Gem::Dependency
|
43
|
-
name: bundler
|
44
|
-
requirement: !ruby/object:Gem::Requirement
|
45
|
-
requirements:
|
46
|
-
- - ~>
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
version: '1.11'
|
49
|
-
type: :development
|
50
|
-
prerelease: false
|
51
|
-
version_requirements: !ruby/object:Gem::Requirement
|
52
|
-
requirements:
|
53
|
-
- - ~>
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version: '1.11'
|
42
|
+
version: 2.2.0
|
56
43
|
- !ruby/object:Gem::Dependency
|
57
44
|
name: rake
|
58
45
|
requirement: !ruby/object:Gem::Requirement
|
59
46
|
requirements:
|
60
|
-
- - ~>
|
47
|
+
- - "~>"
|
61
48
|
- !ruby/object:Gem::Version
|
62
|
-
version:
|
49
|
+
version: 12.3.2
|
63
50
|
type: :development
|
64
51
|
prerelease: false
|
65
52
|
version_requirements: !ruby/object:Gem::Requirement
|
66
53
|
requirements:
|
67
|
-
- - ~>
|
54
|
+
- - "~>"
|
68
55
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
56
|
+
version: 12.3.2
|
70
57
|
- !ruby/object:Gem::Dependency
|
71
58
|
name: rspec
|
72
59
|
requirement: !ruby/object:Gem::Requirement
|
73
60
|
requirements:
|
74
|
-
- - ~>
|
61
|
+
- - "~>"
|
75
62
|
- !ruby/object:Gem::Version
|
76
|
-
version:
|
63
|
+
version: 3.8.0
|
77
64
|
type: :development
|
78
65
|
prerelease: false
|
79
66
|
version_requirements: !ruby/object:Gem::Requirement
|
80
67
|
requirements:
|
81
|
-
- - ~>
|
68
|
+
- - "~>"
|
82
69
|
- !ruby/object:Gem::Version
|
83
|
-
version:
|
84
|
-
description: This gem simplifies generating the claims
|
85
|
-
Atlassian Connect REST APIs.
|
70
|
+
version: 3.8.0
|
71
|
+
description: This gem simplifies generating the claims needed to authenticate with
|
72
|
+
the Atlassian Connect REST APIs.
|
86
73
|
email:
|
87
74
|
- spike@6kites.com
|
88
|
-
-
|
75
|
+
- seb@sebruiz.net
|
76
|
+
- ndao@atlassian.com
|
89
77
|
executables: []
|
90
78
|
extensions: []
|
91
79
|
extra_rdoc_files: []
|
92
80
|
files:
|
93
|
-
- .gitignore
|
94
|
-
- .rspec
|
81
|
+
- ".gitignore"
|
82
|
+
- ".rspec"
|
95
83
|
- Gemfile
|
84
|
+
- LICENSE.txt
|
96
85
|
- README.md
|
97
86
|
- Rakefile
|
98
87
|
- atlassian-jwt.gemspec
|
@@ -110,17 +99,16 @@ require_paths:
|
|
110
99
|
- lib
|
111
100
|
required_ruby_version: !ruby/object:Gem::Requirement
|
112
101
|
requirements:
|
113
|
-
- -
|
102
|
+
- - ">="
|
114
103
|
- !ruby/object:Gem::Version
|
115
104
|
version: '0'
|
116
105
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
106
|
requirements:
|
118
|
-
- -
|
107
|
+
- - ">="
|
119
108
|
- !ruby/object:Gem::Version
|
120
109
|
version: '0'
|
121
110
|
requirements: []
|
122
|
-
|
123
|
-
rubygems_version: 2.0.14.1
|
111
|
+
rubygems_version: 3.0.1
|
124
112
|
signing_key:
|
125
113
|
specification_version: 4
|
126
114
|
summary: Encode and decode JWT tokens for use with the Atlassian Connect REST APIs.
|