jwt_api 0.1.0 → 0.1.4
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 +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +3 -2
- data/README.md +14 -13
- data/jwt_api-0.1.2.gem +0 -0
- data/jwt_api-0.1.3.gem +0 -0
- data/jwt_api.gemspec +2 -2
- data/lib/generators/jwt_api/templates/api/base_controller.rb +13 -16
- data/lib/generators/jwt_api/templates/api/v1/authentication_controller.rb +5 -4
- data/lib/generators/jwt_api/templates/api/v1/passwords_controller.rb +16 -20
- data/lib/generators/jwt_api/templates/api/v1/users_controller.rb +1 -0
- data/lib/generators/jwt_api/templates/initializers/json_web_token.rb +12 -6
- data/lib/jwt_api/version.rb +1 -1
- data/lib/jwt_api.rb +0 -29
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7772f9c066af82798657d3761b4e2591a5d3bc9683983fe5f230bbd063c1d8d8
|
4
|
+
data.tar.gz: 0b7e3c073bd3480c48be3e7610b0d5bc25c75dfdcc1f22e17bade00fc50f9e47
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59bf83ec8f2afeea4a824a69783dad05f20ccea2895ae608574b391253b4a1b6d089ffb9ab2deeac95c87f623839998e55fdcf8fd63f76fce16c0f6384b4a91a
|
7
|
+
data.tar.gz: f8a201feaf0ea3c2a0443b4a6c6a50e8931f8b57c60fc57e2ae17d818e2d6ee19ff24a5d3646010df15520e55ffe5debeaf3610323df59dcfa6add06fee33481
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
jwt_api (0.1.
|
4
|
+
jwt_api (0.1.1)
|
5
5
|
jwt (~> 2.2, >= 2.2.3)
|
6
6
|
|
7
7
|
GEM
|
@@ -25,13 +25,14 @@ GEM
|
|
25
25
|
rspec-support (3.10.2)
|
26
26
|
|
27
27
|
PLATFORMS
|
28
|
+
arm64-darwin-21
|
28
29
|
x86_64-darwin-20
|
29
30
|
|
30
31
|
DEPENDENCIES
|
31
32
|
jwt (~> 2.2, >= 2.2.3)
|
32
33
|
jwt_api!
|
33
34
|
rake (~> 13.0)
|
34
|
-
rspec
|
35
|
+
rspec
|
35
36
|
|
36
37
|
BUNDLED WITH
|
37
38
|
2.2.25
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# JwtApi
|
2
2
|
|
3
|
-
|
3
|
+
Scaffold a JSON Web Token API.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -51,13 +51,22 @@ Running via Spring preloader in process 56250
|
|
51
51
|
== 20210827123255 AddJtiToUsers: migrated (0.0259s) ===========================
|
52
52
|
```
|
53
53
|
|
54
|
-
|
54
|
+
|
55
55
|
## Usage
|
56
56
|
|
57
57
|
1. Make sure that each user that needs access to the API has a JTI generated.
|
58
58
|
1. `User.first.jti = SecureRandom.uuid`
|
59
|
-
2.
|
60
|
-
|
59
|
+
2. From your Rails console run `SecureRandom.hex(64)` and make note of the output.
|
60
|
+
- Sample output:
|
61
|
+
```text
|
62
|
+
"0086870fb04cafbaa15b110cf78352fbca75537cc90e06892e206e07c24caa33ff5f6aadf2649cafac08c4acf6a1b7527b97bfa943481c282ba2480a0a922657"
|
63
|
+
```
|
64
|
+
3. Run `rails credentials:edit --environment=development` (and production, staging when applicable) and set your `jwt_secret` environment variable.
|
65
|
+
```yml
|
66
|
+
jwt_secret: 0086870fb04cafbaa15b110cf78352fbca75537cc90e06892e206e07c24caa33ff5f6aadf2649cafac08c4acf6a1b7527b97bfa943481c282ba2480a0a922657
|
67
|
+
```
|
68
|
+
3. Request a JWT at the `/api/authenticate/` endpoint.
|
69
|
+
4. Include that token as a `Bearer` token in all other requests.
|
61
70
|
|
62
71
|
|
63
72
|
[](https://app.getpostman.com/run-collection/6130650-059cc2e3-88f7-48a8-95d0-d7dca1d7caef?action=collection%2Ffork&collection-url=entityId%3D6130650-059cc2e3-88f7-48a8-95d0-d7dca1d7caef%26entityType%3Dcollection%26workspaceId%3D128e0ba1-898b-40bb-8006-a329fb1c28de)
|
@@ -67,11 +76,6 @@ Feel free to remove the gem from your Gemfile, you now have everything you need
|
|
67
76
|
|
68
77
|
## Limitations
|
69
78
|
- Currently this will only work with a devise User model.
|
70
|
-
## Development
|
71
|
-
|
72
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
73
|
-
|
74
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
75
79
|
|
76
80
|
## Contributing
|
77
81
|
|
@@ -83,8 +87,5 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
83
87
|
|
84
88
|
## Code of Conduct
|
85
89
|
|
86
|
-
Everyone interacting in the JwtApi project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
87
|
-
|
90
|
+
Everyone interacting in the JwtApi project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/leopolicastro/jwt_api/blob/main/CODE_OF_CONDUCT.md).
|
88
91
|
|
89
|
-
## TODO
|
90
|
-
- Create test suite and add to generator to copy into users app.
|
data/jwt_api-0.1.2.gem
ADDED
Binary file
|
data/jwt_api-0.1.3.gem
ADDED
Binary file
|
data/jwt_api.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.authors = ['Leo Policastro']
|
9
9
|
spec.email = ['lpolicastro@pm.me']
|
10
10
|
|
11
|
-
spec.summary = '
|
12
|
-
spec.description = '
|
11
|
+
spec.summary = 'Scaffold a JSON Web Token API'
|
12
|
+
spec.description = 'Scaffold a JSON Web Token API.'
|
13
13
|
spec.homepage = 'https://github.com/leopolicastro/jwt_api/'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
spec.required_ruby_version = '>= 2.4.0'
|
@@ -8,11 +8,7 @@ class Api::BaseController < ApplicationController
|
|
8
8
|
protected
|
9
9
|
|
10
10
|
def authenticate_request!
|
11
|
-
|
12
|
-
render json: { errors: ['Unauthorized'] }, status: :unauthorized
|
13
|
-
return
|
14
|
-
end
|
15
|
-
@current_user = User.find(auth_token[:user_id])
|
11
|
+
http_token && auth_token && jti_matches?
|
16
12
|
rescue JWT::VerificationError, JWT::DecodeError
|
17
13
|
render json: { errors: ['Unauthorized'] }, status: :unauthorized
|
18
14
|
end
|
@@ -20,27 +16,28 @@ class Api::BaseController < ApplicationController
|
|
20
16
|
private
|
21
17
|
|
22
18
|
def http_token
|
23
|
-
@http_token ||= if request.headers['Authorization'].present?
|
24
|
-
request.headers['Authorization'].split.last
|
25
|
-
end
|
19
|
+
@http_token ||= (request.headers['Authorization'].split.last if request.headers['Authorization'].present?)
|
26
20
|
end
|
27
21
|
|
28
22
|
def auth_token
|
29
|
-
@auth_token ||=
|
30
|
-
|
31
|
-
render json: { error: 'token expired' }
|
23
|
+
@auth_token ||= jwt.decode(http_token)[0].to_h.symbolize_keys!
|
24
|
+
return @auth_token if @auth_token.present? && @auth_token[:user_id].present? && !token_expired?
|
32
25
|
end
|
33
26
|
|
34
|
-
def
|
35
|
-
@
|
36
|
-
!@current_user.jti.nil? && @current_user.jti == auth_token[:jti]
|
27
|
+
def token_expired?
|
28
|
+
@auth_token[:exp] < Time.now.to_i
|
37
29
|
end
|
38
30
|
|
39
|
-
def
|
40
|
-
|
31
|
+
def jti_matches?
|
32
|
+
@current_user = User.find(@auth_token[:user_id])
|
33
|
+
@current_user&.jti == @auth_token[:jti]
|
41
34
|
end
|
42
35
|
|
43
36
|
def user_reset_token_in_params?
|
44
37
|
params[:reset_password_token]
|
45
38
|
end
|
39
|
+
|
40
|
+
def jwt
|
41
|
+
JsonWebToken.new
|
42
|
+
end
|
46
43
|
end
|
@@ -29,11 +29,12 @@ class Api::V1::AuthenticationController < Api::BaseController
|
|
29
29
|
|
30
30
|
iat = Time.now.to_i
|
31
31
|
exp = Time.now.to_i + 24 * 3600
|
32
|
+
|
32
33
|
{
|
33
|
-
token:
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
token: jwt.encode({ user_id: user.id,
|
35
|
+
jti: user.jti,
|
36
|
+
iat: iat,
|
37
|
+
exp: exp })
|
37
38
|
}
|
38
39
|
end
|
39
40
|
end
|
@@ -11,15 +11,14 @@ class Api::V1::PasswordsController < Api::BaseController
|
|
11
11
|
@user = User.find_by(email: password_params[:email])
|
12
12
|
if @user.nil?
|
13
13
|
render json: { message: 'email not found' }, status: :not_found
|
14
|
+
elsif @user.update(
|
15
|
+
reset_password_token: SecureRandom.uuid,
|
16
|
+
reset_password_sent_at: Time.now
|
17
|
+
)
|
18
|
+
JwtMailer.reset_password(@user.id, @user.reset_password_token).deliver
|
19
|
+
render json: { message: 'reset password instructions sent' }, status: :ok
|
14
20
|
else
|
15
|
-
@user.
|
16
|
-
@user.reset_password_sent_at = Time.now
|
17
|
-
if @user.save
|
18
|
-
JwtMailer.reset_password(@user.id, @user.reset_password_token).deliver
|
19
|
-
render json: { message: 'reset password instructions sent' }, status: :ok
|
20
|
-
else
|
21
|
-
render json: { message: @user.errors }, status: :not_found
|
22
|
-
end
|
21
|
+
render json: { message: @user.errors }, status: :not_found
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
@@ -27,25 +26,22 @@ class Api::V1::PasswordsController < Api::BaseController
|
|
27
26
|
# with a token in the params, if a succesful response is received, the client can
|
28
27
|
# store the newly issued JWT and redirect the user to the password reset form
|
29
28
|
def verify
|
30
|
-
@user = User.
|
29
|
+
@user = User.find_by(reset_password_token: params[:token])
|
31
30
|
if @user.nil?
|
32
31
|
render json: { message: 'reset password token not found' }, status: :not_found
|
33
|
-
elsif @user.reset_password_sent_at <
|
32
|
+
elsif @user.reset_password_sent_at < 10.minutes.ago
|
34
33
|
render json: { message: 'reset password token has expired' }, status: :not_found
|
35
34
|
else
|
36
|
-
@user.
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
iat = Time.now.to_i
|
42
|
-
exp = Time.now.to_i + 10 * 60
|
43
|
-
|
35
|
+
@user.update!(
|
36
|
+
reset_password_token: nil,
|
37
|
+
reset_password_sent_at: nil,
|
38
|
+
jti: SecureRandom.uuid
|
39
|
+
)
|
44
40
|
render json: {
|
45
41
|
token: JsonWebToken.encode({ user_id: @user.id,
|
46
42
|
jti: @user.jti,
|
47
|
-
iat:
|
48
|
-
exp:
|
43
|
+
iat: Time.now.to_i,
|
44
|
+
exp: Time.now.to_i + 10 * 60 })
|
49
45
|
}, status: :ok
|
50
46
|
end
|
51
47
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
# User controller
|
4
4
|
class Api::V1::UsersController < Api::BaseController
|
5
5
|
skip_before_action :authenticate_request!, only: %i[create]
|
6
|
+
|
6
7
|
def create
|
7
8
|
unless user_params[:password] == user_params[:password_confirmation]
|
8
9
|
return render json: { message: "passwords don't match" }, status: :unprocessable_entity
|
@@ -1,11 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# JSON Web Token class
|
1
4
|
class JsonWebToken
|
2
|
-
def
|
3
|
-
|
5
|
+
def initialize(key = Rails.application.credentials[:jwt_secret], algorithm = 'HS256')
|
6
|
+
@key = key
|
7
|
+
@algorithm = algorithm
|
8
|
+
end
|
9
|
+
|
10
|
+
def encode(payload)
|
11
|
+
JWT.encode(payload, @key, @algorithm)
|
4
12
|
end
|
5
13
|
|
6
|
-
def
|
7
|
-
|
8
|
-
rescue StandardError
|
9
|
-
nil
|
14
|
+
def decode(token)
|
15
|
+
JWT.decode(token, @key, @algorithm)
|
10
16
|
end
|
11
17
|
end
|
data/lib/jwt_api/version.rb
CHANGED
data/lib/jwt_api.rb
CHANGED
@@ -2,32 +2,3 @@
|
|
2
2
|
|
3
3
|
require 'jwt'
|
4
4
|
require_relative 'jwt_api/version'
|
5
|
-
|
6
|
-
module JwtApi
|
7
|
-
class Jwt
|
8
|
-
def initialize(key, algorithm = 'HS256')
|
9
|
-
@key = key
|
10
|
-
@algorithm = algorithm
|
11
|
-
end
|
12
|
-
|
13
|
-
def encode(payload)
|
14
|
-
JWT.encode(payload, @key, @algorithm)
|
15
|
-
end
|
16
|
-
|
17
|
-
def decode(token)
|
18
|
-
JWT.decode(token, @key, @algorithm)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
class JsonWebToken
|
24
|
-
def self.encode(payload)
|
25
|
-
JWT.encode(payload, Rails.application.secrets.secret_key_base)
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.decode(token)
|
29
|
-
HashWithIndifferentAccess.new(JWT.decode(token, Rails.application.secrets.secret_key_base)[0])
|
30
|
-
rescue StandardError
|
31
|
-
nil
|
32
|
-
end
|
33
|
-
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jwt_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Leo Policastro
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jwt
|
@@ -30,7 +30,7 @@ dependencies:
|
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 2.2.3
|
33
|
-
description:
|
33
|
+
description: Scaffold a JSON Web Token API.
|
34
34
|
email:
|
35
35
|
- lpolicastro@pm.me
|
36
36
|
executables: []
|
@@ -48,6 +48,8 @@ files:
|
|
48
48
|
- Rakefile
|
49
49
|
- bin/console
|
50
50
|
- bin/setup
|
51
|
+
- jwt_api-0.1.2.gem
|
52
|
+
- jwt_api-0.1.3.gem
|
51
53
|
- jwt_api.gemspec
|
52
54
|
- lib/generators/jwt_api/setup_generator.rb
|
53
55
|
- lib/generators/jwt_api/templates/api/base_controller.rb
|
@@ -83,8 +85,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
85
|
- !ruby/object:Gem::Version
|
84
86
|
version: '0'
|
85
87
|
requirements: []
|
86
|
-
rubygems_version: 3.
|
88
|
+
rubygems_version: 3.3.7
|
87
89
|
signing_key:
|
88
90
|
specification_version: 4
|
89
|
-
summary:
|
91
|
+
summary: Scaffold a JSON Web Token API
|
90
92
|
test_files: []
|