jwt-rest 0.1.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +72 -0
- data/Rakefile +33 -0
- data/lib/jwt_rest/auth_header.rb +25 -0
- data/lib/jwt_rest/authenticable.rb +30 -0
- data/lib/jwt_rest/secrets.rb +21 -0
- data/lib/jwt_rest/tokens/basic.rb +31 -0
- data/lib/jwt_rest/tokens/jwt.rb +34 -0
- data/lib/jwt_rest/version.rb +3 -0
- data/lib/jwt_rest.rb +10 -0
- data/lib/tasks/jwt/rest_tasks.rake +4 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6229e085830cca4899bfa9ef248f02d8ad7fca11
|
4
|
+
data.tar.gz: ef6d83d44c95ae05d62ff1a766761a3ac1e8fe8c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c1a9043a63577267151a44f191249b12945ffde205baddd09c4fffa4773b1a40bbcd693beb08b11e759dda3170ec67efe8b0d07a69d7a7bd3ab60d66156b120f
|
7
|
+
data.tar.gz: '0900893019db66dbf674673896d4115b64d6c0ec9ce6765f967c872f1738d25b9cf19492b908c5ca19131740aa36c0b0623be5fb126e5b321c09b1a735fa54dd'
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2018 mariowise
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# jwt-rest
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
Add this line to your application's Gemfile:
|
5
|
+
|
6
|
+
```ruby
|
7
|
+
gem 'jwt-rest'
|
8
|
+
```
|
9
|
+
|
10
|
+
And then execute:
|
11
|
+
```bash
|
12
|
+
$ bundle
|
13
|
+
```
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
First you will need to provide a way to find if the ApiKey is valid or not, and a way to retrieve the RSA private key to sign JWT tokens. For this, lets create a initializer `config/initializers/jwt_rest.rb`.
|
18
|
+
|
19
|
+
```
|
20
|
+
require "jwt_rest"
|
21
|
+
|
22
|
+
module JwtRest
|
23
|
+
module Secrets
|
24
|
+
def self.rsa_private_key
|
25
|
+
# return he Base64 encoded of your RSA private key
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.valid_api_key?(api_key)
|
29
|
+
# return true if the api_key is valid
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
Then in your API's base controller do this
|
36
|
+
|
37
|
+
```
|
38
|
+
class ApiController < ActionController::API
|
39
|
+
include JwtRest::Authenticable
|
40
|
+
|
41
|
+
before_action :demand_application_json
|
42
|
+
before_action :demand_api_key
|
43
|
+
|
44
|
+
def handle_user_identity(jwt_payload)
|
45
|
+
return false unless @current_user = User.find_by(email: jwt_payload.dig("email"))
|
46
|
+
true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
This will ensure that every call to your API i
|
52
|
+
s made with a valid api key. You could use the method `demand_current_user` for those endpoins where you need to authenticate the user with the JWT token.
|
53
|
+
|
54
|
+
```
|
55
|
+
class UsersController < ApiController
|
56
|
+
before_action :demand_current_user, only: [:profile]
|
57
|
+
|
58
|
+
def profile
|
59
|
+
# here we have the @current_user variable
|
60
|
+
end
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
## Contributing
|
65
|
+
Contribution directions go here.
|
66
|
+
|
67
|
+
### TODO
|
68
|
+
|
69
|
+
* Multiple RSA algorithm types for the JWT token
|
70
|
+
|
71
|
+
## License
|
72
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Jwt::Rest'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
require 'bundler/gem_tasks'
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'test'
|
28
|
+
t.pattern = 'test/**/*_test.rb'
|
29
|
+
t.verbose = false
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
task default: :test
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module JwtRest
|
2
|
+
class AuthHeader
|
3
|
+
def initialize(chunk = "")
|
4
|
+
@type, @value = chunk.split(" ")
|
5
|
+
@type = @type&.downcase
|
6
|
+
end
|
7
|
+
|
8
|
+
def is_basic?
|
9
|
+
type == "basic"
|
10
|
+
end
|
11
|
+
|
12
|
+
def is_token?
|
13
|
+
type == "bearer"
|
14
|
+
end
|
15
|
+
|
16
|
+
def token
|
17
|
+
return JwtRest::Tokens::Basic.new(token: value).load_token if is_basic?
|
18
|
+
return JwtRest::Tokens::Jwt.new(token: value).load_token if is_token?
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_accessor :type, :value
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module JwtRest
|
2
|
+
module Authenticable
|
3
|
+
def demand_application_json
|
4
|
+
unless request.format.symbol == :json
|
5
|
+
render status: :not_acceptable, json: { error: "only application/json Content-Tyle is allowed" }
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def demand_api_key
|
10
|
+
api_key = request.headers["HTTP_X_API_KEY"]
|
11
|
+
unless JwtRest::Secrets.valid_api_key?(api_key)
|
12
|
+
render status: :unauthorized, json: { error: "invalid api key" }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def demand_current_user
|
17
|
+
header = JwtRest::AuthHeader.new(request.headers["HTTP_AUTHORIZATION"])
|
18
|
+
unless header.is_token? && header.token && handle_user_identity(header.token.payload)
|
19
|
+
render status: :unauthorized, json: { error: "invalid authorization token" }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# To be defined by the developer
|
24
|
+
# def handle_user_identity(jwt_payload)
|
25
|
+
# true if the users is valid
|
26
|
+
# end
|
27
|
+
|
28
|
+
attr_reader :current_user
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module JwtRest
|
2
|
+
module Secrets
|
3
|
+
def self.rsa_private_key
|
4
|
+
raise "You should override this method and provide the Base64 encoded of your RSA private key"
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.valid_api_key?(api_key)
|
8
|
+
raise "You should override this method and provide a way to verify your API_KEYs"
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.rsa_private
|
12
|
+
pkey = rsa_private_key
|
13
|
+
begin
|
14
|
+
decoded = Base64.decode64(pkey)
|
15
|
+
OpenSSL::PKey::RSA.new(decoded)
|
16
|
+
rescue
|
17
|
+
raise "Unable to decode your private key. Be sure to provide a valid RSA private key and then apply the Base64 encoding"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module JwtRest
|
2
|
+
module Tokens
|
3
|
+
class Basic
|
4
|
+
attr_reader :username, :password, :token
|
5
|
+
|
6
|
+
def initialize(username: nil, password: nil, token: nil)
|
7
|
+
@username = username
|
8
|
+
@password = password
|
9
|
+
@token = token
|
10
|
+
end
|
11
|
+
|
12
|
+
def load_token
|
13
|
+
@username, @password = decoder(@token).split(":")
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def load_credentials
|
18
|
+
@token = encoder("#{username}:#{password}")
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def encoder(target)
|
23
|
+
Base64.encode64(target).gsub("\n", "")
|
24
|
+
end
|
25
|
+
|
26
|
+
def decoder(target)
|
27
|
+
Base64.decode64(target)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module JwtRest
|
2
|
+
module Tokens
|
3
|
+
class JwtRest::Tokens::Jwt
|
4
|
+
attr_reader :payload, :token
|
5
|
+
|
6
|
+
def initialize(payload: nil, token: nil)
|
7
|
+
@payload = payload || {}
|
8
|
+
@token = token
|
9
|
+
end
|
10
|
+
|
11
|
+
def load_payload
|
12
|
+
@payload[:exp] = default_exp unless @payload[:exp]
|
13
|
+
@token = JWT.encode(payload, key, 'RS384')
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def load_token
|
18
|
+
parts = JWT.decode(token, key.public_key, true, { algorithm: 'RS384' })
|
19
|
+
@payload = parts.reduce({}, :merge)
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def key
|
26
|
+
JwtRest::Secrets.rsa_private
|
27
|
+
end
|
28
|
+
|
29
|
+
def default_exp
|
30
|
+
(Time.now + 24 * 3600).to_i
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/jwt_rest.rb
ADDED
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jwt-rest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- mariowise
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-05-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.1.6
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.1.6
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jwt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.1.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.1.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pry
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: A security layer for any API REST in Rails with JWT tokens.
|
84
|
+
email:
|
85
|
+
- mariolopezlandes@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- MIT-LICENSE
|
91
|
+
- README.md
|
92
|
+
- Rakefile
|
93
|
+
- lib/jwt_rest.rb
|
94
|
+
- lib/jwt_rest/auth_header.rb
|
95
|
+
- lib/jwt_rest/authenticable.rb
|
96
|
+
- lib/jwt_rest/secrets.rb
|
97
|
+
- lib/jwt_rest/tokens/basic.rb
|
98
|
+
- lib/jwt_rest/tokens/jwt.rb
|
99
|
+
- lib/jwt_rest/version.rb
|
100
|
+
- lib/tasks/jwt/rest_tasks.rake
|
101
|
+
homepage: https://github.com/mariowise/jwt-rest
|
102
|
+
licenses:
|
103
|
+
- MIT
|
104
|
+
metadata: {}
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
requirements: []
|
120
|
+
rubyforge_project:
|
121
|
+
rubygems_version: 2.5.1
|
122
|
+
signing_key:
|
123
|
+
specification_version: 4
|
124
|
+
summary: A security layer for any API REST in Rails with JWT tokens.
|
125
|
+
test_files: []
|