atlassian-jwt 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 95822a295a203005eef8ab806c8271e186e79e60
4
+ data.tar.gz: d86ec07e14e5ac7b4e1328b94026e38f84520e95
5
+ SHA512:
6
+ metadata.gz: 5ca63e222c259304cbfd59a9bdc93c7748afbfe7f99ccff349044a64ce28625e5c2fbb95329db43803118613e211d350e8e508942f3bc4daf4ed9ee5a9d67826
7
+ data.tar.gz: a7596769f75d9f03d904c47fbb75ef5db2080b5ead8a395b26d5289f2be4e2e574a2b4cccba6b69d9352aa245eb5c42a33db2c72fc640d9b06bb9868477454ae
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /.ruby-*
11
+ /atlassian-jwt-*.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in atlassian-jwt.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # Atlassian::Jwt
2
+
3
+ This gem provides helpers for generating Atlassian specific JWT
4
+ claims. It also exposes the [ruby-jwt](https://github.com/jwt/ruby-jwt)
5
+ gem's `encode` and `decode` methods.
6
+
7
+ [![build-status](https://bitbucket-badges.useast.atlassian.io/badge/atlassian/atlassian-jwt-ruby.svg)](https://bitbucket.org/atlassian/atlassian-jwt-ruby/addon/pipelines/home)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'atlassian-jwt'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install atlassian-jwt
24
+
25
+ ## Usage
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.
33
+
34
+ ### Generating a JWT Token
35
+
36
+ ```ruby
37
+ require 'atlassian/jwt'
38
+
39
+ # The URL of the API call, must include the query string, if any
40
+ url = 'https://jira.atlassian.com/rest/api/latest/issue/JRA-9'
41
+ # The key of the add-on as defined in the add-on description
42
+ issuer = 'com.atlassian.example'
43
+ http_method = 'get' # The HTTP Method (GET, POST, etc) of the API call
44
+ shared_secret = '...' # "sharedSecret", returned when the add-on is installed.
45
+
46
+ claim = Atlassian::Jwt.build_claims(issuer,url,http_method)
47
+ jwt = JWT.encode(claim,shared_secret)
48
+ ```
49
+
50
+ If the base URL of the API is not at the root of the site,
51
+ i.e. *https://site.atlassian.net/jira/rest/api*, you will need to pass
52
+ in the base URL to `#.build_claims`:
53
+
54
+ ```
55
+ url = 'https://site.atlassian.net/jira/rest/api/latest/issue/JRA-9'
56
+ base_url = 'https://site.atlassian.net'
57
+
58
+ claim = Atlassian::Jwt.build_claims(issuer, url, http_method, base_url)
59
+ ```
60
+
61
+ The generated JWT can then be passed in an 'Authentication' header or
62
+ in the query string:
63
+
64
+ ```ruby
65
+ # Header
66
+ uri = URI('https://site.atlassian.net/rest/api/latest/issue/JRA-9')
67
+ http = Net::HTTP.new(uri.host, uri.port)
68
+ request = Net::HTTP::Get.new(uri.request_uri)
69
+ request.initialize_http_header({'Authentication' => "JWT #{jwt}"})
70
+ response = http.request(request)
71
+ ```
72
+
73
+ ```ruby
74
+ # Query String
75
+ uri = URI("https://site.atlassian.net/rest/api/latest/issue/JRA-9?jwt=#{jwt}")
76
+ http = Net::HTTP.new(uri.host, uri.port)
77
+ request = Net::HTTP::Get.new(uri.request_uri)
78
+ response = http.request(request)
79
+ ```
80
+
81
+ By default the issue time of the claim is now and the expiration is 60
82
+ seconds in the future, these can be overridden:
83
+
84
+ ```ruby
85
+ claim = Atlassian::Jwt.build_claims(
86
+ issuer,
87
+ url,
88
+ http_method,
89
+ base_url,
90
+ Time.now - 60.seconds
91
+ Time.now + 1.day
92
+ )
93
+ ```
94
+
95
+ ### Decoding a JWT token
96
+
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 claim
99
+ and the second being the JWT header, which contains information about
100
+ how the JWT was encoded.
101
+
102
+ ```ruby
103
+ claims, jwt_header = Atlassian::Jwt.decode(params[:jwt], shared_secret)
104
+ ```
105
+
106
+ 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
108
+ 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
110
+ `false` to tell the gem to to verify the signature.
111
+
112
+ ```ruby
113
+ claims, jwt_header = Atlassian::Jwt.decode(params[:jwt], nil, false)
114
+ ```
115
+
116
+ See the [ruby-jwt doc](https://github.com/jwt/ruby-jwt) for additional
117
+ details.
118
+
119
+ ## Development
120
+
121
+ After checking out the repo, run `bin/setup` to install dependencies. Then,
122
+ run `rake spec` to run the tests. You can also run `bin/console` for an
123
+ interactive prompt that will allow you to experiment.
124
+
125
+ To install this gem onto your local machine, run `bundle exec rake install`.
126
+ To release a new version, update the version number in `version.rb`, and
127
+ then run `bundle exec rake release`, which will create a git tag for the
128
+ version, push git commits and tags, and push the `.gem` file to
129
+ [rubygems.org](https://rubygems.org).
130
+
131
+ ## Contributing
132
+
133
+ Bug reports and pull requests are welcome on Bitbucket at
134
+ https://bitbucket.org/atlassian/atlassian-jwt-ruby.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'atlassian/jwt/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "atlassian-jwt"
8
+ spec.version = Atlassian::Jwt::VERSION
9
+ spec.authors = ["Spike Ilacqua", "Seb Ruiz"]
10
+ spec.email = ["spike@6kites.com", "sruiz@atlassian.com"]
11
+
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 need to authenticate with the Atlassian Connect REST APIs.}
14
+ spec.homepage = "https://bitbucket.org/atlassian/atlassian-jwt-ruby"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency 'jwt', '~> 1.5'
22
+ spec.add_development_dependency 'json', '~> 1.8'
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.11"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.0"
27
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "atlassian/jwt"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,14 @@
1
+ # This is a sample build configuration for Ruby.
2
+ # Only use spaces to indent your .yml configuration.
3
+ # -----
4
+ # You can specify a custom docker image from Dockerhub as your build environment.
5
+ image: ruby:2.3.0
6
+
7
+ pipelines:
8
+ default:
9
+ - step:
10
+ script: # Modify the commands below to build your repository.
11
+ - ruby --version
12
+ - bundler --version
13
+ - bundle install
14
+ - rake
@@ -0,0 +1,76 @@
1
+ require 'atlassian/jwt/version'
2
+ require 'jwt'
3
+ require 'uri'
4
+ require 'cgi'
5
+
6
+ module Atlassian
7
+ module Jwt
8
+ class << self
9
+ CANONICAL_QUERY_SEPARATOR = '&'
10
+ ESCAPED_CANONICAL_QUERY_SEPARATOR = '%26'
11
+
12
+ def decode(token,secret, validate = true, options = {})
13
+ options = { :algorithm => 'HS256' }.merge(options)
14
+ ::JWT.decode token, secret, validate, options
15
+ end
16
+
17
+ def encode(payload, secret, algorithm = 'HS256', header_fields = {})
18
+ ::JWT.encode payload, secret, algorithm, header_fields
19
+ end
20
+
21
+ def create_query_string_hash(uri, http_method, base_uri)
22
+ Digest::SHA256.hexdigest(
23
+ create_canonical_request(uri, http_method, base_uri)
24
+ )
25
+ end
26
+
27
+ def create_canonical_request(uri, http_method, base_uri)
28
+ uri = URI.parse(uri) unless uri.kind_of? URI
29
+ base_uri = URI.parse(base_uri) unless base_uri.kind_of? URI
30
+
31
+ path = canonicalize_uri(uri, base_uri)
32
+
33
+ [http_method.upcase,
34
+ canonicalize_uri(uri, base_uri),
35
+ canonicalize_query_string(uri.query)
36
+ ].join(CANONICAL_QUERY_SEPARATOR)
37
+ end
38
+
39
+ def build_claims(issuer,url,http_method,base_url='',issued_at=nil,expires=nil,attributes={})
40
+ issued_at ||= Time.now.to_i
41
+ expires ||= issued_at + 60
42
+ qsh = Digest::SHA256.hexdigest(
43
+ Atlassian::Jwt.create_canonical_request(url,http_method,base_url)
44
+ )
45
+
46
+ {
47
+ iss: issuer,
48
+ iat: issued_at,
49
+ exp: expires,
50
+ qsh: qsh
51
+ }.merge(attributes)
52
+ end
53
+
54
+ def canonicalize_uri(uri, base_uri)
55
+ path = uri.path.sub(/^#{base_uri.path}/,'')
56
+ path = '/' if path.nil? || path.empty?
57
+ path = '/' + path unless path.start_with? '/'
58
+ path.chomp!('/') if path.length > 1
59
+ path.gsub(CANONICAL_QUERY_SEPARATOR, ESCAPED_CANONICAL_QUERY_SEPARATOR)
60
+ end
61
+
62
+ def canonicalize_query_string(query)
63
+ return '' if query.nil? || query.empty?
64
+ query = CGI::parse(query)
65
+ query.delete('jwt')
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') # Use %20, not CGI.escape default of "+"
69
+ query[k].gsub!('%7E','~') # Unescape "~" per JS tests
70
+ end
71
+ query = Hash[query.sort]
72
+ query.map {|k,v| "#{CGI.escape k}=#{v}" }.join('&')
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,5 @@
1
+ module Atlassian
2
+ module Jwt
3
+ VERSION = "0.1.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: atlassian-jwt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Spike Ilacqua
8
+ - Seb Ruiz
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2016-08-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: jwt
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ~>
19
+ - !ruby/object:Gem::Version
20
+ version: '1.5'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ version: '1.5'
28
+ - !ruby/object:Gem::Dependency
29
+ name: json
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: '1.8'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ version: '1.8'
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'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rake
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ version: '10.0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '10.0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rspec
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: '3.0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ~>
82
+ - !ruby/object:Gem::Version
83
+ version: '3.0'
84
+ description: This gem simplifies generating the claims need to authenticate with the
85
+ Atlassian Connect REST APIs.
86
+ email:
87
+ - spike@6kites.com
88
+ - sruiz@atlassian.com
89
+ executables: []
90
+ extensions: []
91
+ extra_rdoc_files: []
92
+ files:
93
+ - .gitignore
94
+ - .rspec
95
+ - Gemfile
96
+ - README.md
97
+ - Rakefile
98
+ - atlassian-jwt.gemspec
99
+ - bin/console
100
+ - bin/setup
101
+ - bitbucket-pipelines.yml
102
+ - lib/atlassian/jwt.rb
103
+ - lib/atlassian/jwt/version.rb
104
+ homepage: https://bitbucket.org/atlassian/atlassian-jwt-ruby
105
+ licenses: []
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 2.0.14.1
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: Encode and decode JWT tokens for use with the Atlassian Connect REST APIs.
127
+ test_files: []