credible 0.11.0 → 0.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3600f170c11397baf11f7b350fdc1bcfe5fa9eff0c447743e145affdfb33d2d2
4
- data.tar.gz: 1ab495351756b77d942082e1c31c4327154041575e9d1ce35ee519943d9edd7c
3
+ metadata.gz: 36d4cd634fa34562978e37d5a84169812678c28109d09c58238cb8c5bc2338d5
4
+ data.tar.gz: edc9471b968557985508ed60f49ae905390b3167d120497b3ea272faed0b423a
5
5
  SHA512:
6
- metadata.gz: 5ed7e26ed44a7e69d70791aee2c910a9ccc80c340bd26541a6f4679427c76a75b31e1ddd308816f20bb4ee3fcd3031d011679364cafb0c91d8b7256be659ab2c
7
- data.tar.gz: bc4bcef987ba1287acc7ead7d6d4c596dd33e6b72f8b5d221352dd81e895be6c0d80f87cd12712f63168b86ab2a3fa916fc8959f15c30c72fe75062b55e6399f
6
+ metadata.gz: 263ef8576aae2c0b1aaad9d1e5710c2eca33e5812accbe1a5d19827b37782bb3a8109ba5a91f77a41fc16a41834d5c4d040358be44017836ed9a22c54ffe6c0c
7
+ data.tar.gz: f23153d8e705f201acba1ff85c31016f2e1a1e9076016a66083ef8fb7da21a740c0bd2d5031ec4cf4e881ed0f352e3ac662a4592f599f9f70b7960ba7ab56ae9
data/README.md CHANGED
@@ -1,18 +1,22 @@
1
- # Credible
1
+ <h1 id="credible" align="center">Credible</h1>
2
2
 
3
- [![Become a Patron](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/thombruce)
3
+ <p align="center"><strong>Credible</strong> | <a href="https://github.com/thombruce/credible-demo">Demo Repo</a> | <a href="https://github.com/thombruce/helvellyn">Helvellyn</a></p>
4
4
 
5
- [![Gem](https://img.shields.io/gem/v/credible?logo=rubygems)](https://rubygems.org/gems/credible)
6
- [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/thombruce/credible/CI?logo=github)](https://github.com/thombruce/credible/actions)
7
- [![Codecov](https://img.shields.io/codecov/c/github/thombruce/credible?logo=codecov)](https://codecov.io/gh/thombruce/credible)
8
- [![GitHub issues](https://img.shields.io/github/issues-raw/thombruce/credible?logo=github)](https://github.com/thombruce/credible/issues)
5
+ <p align="center"><a href="https://www.patreon.com/thombruce"><img src="https://c5.patreon.com/external/logo/become_a_patron_button.png" alt="Become a Patron"></a></p>
9
6
 
10
- [![License](https://img.shields.io/badge/license-MIT-green.svg)](MIT-LICENSE)
11
- [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md)
12
- [![Contributing](https://img.shields.io/badge/contributions-welcome-blue.svg)](CONTRIBUTING.md)
13
- [![Discord](https://img.shields.io/discord/697123984231366716?color=7289da&label=chat&logo=discord)](https://discord.gg/YMU87db)
7
+ <p align="center"><a href="https://rubygems.org/gems/credible"><img src="https://img.shields.io/gem/v/credible?logo=rubygems" alt="Gem"></a>
8
+ <a href="https://github.com/thombruce/credible/actions"><img src="https://img.shields.io/github/workflow/status/thombruce/credible/CI?logo=github" alt="GitHub Workflow Status"></a>
9
+ <a href="https://codecov.io/gh/thombruce/credible"><img src="https://img.shields.io/codecov/c/github/thombruce/credible?logo=codecov" alt="Codecov"></a>
10
+ <a href="https://github.com/thombruce/credible/issues"><img src="https://img.shields.io/github/issues-raw/thombruce/credible?logo=github" alt="GitHub issues"></a></p>
14
11
 
15
- Credible was extracted from [thombruce/helvellyn](https://github.com/thombruce/helvellyn) and is still a work in progress. The goal is to provide JWT and API token based authentication using [Warden](https://github.com/wardencommunity/warden/) for Rails API applications.
12
+ <p align="center"><a href="MIT-LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License"></a>
13
+ <a href="CONTRIBUTING.md"><img src="https://img.shields.io/badge/contributions-welcome-blue.svg" alt="Contributing"></a>
14
+ <a href="https://discord.gg/YMU87db"><img src="https://img.shields.io/discord/697123984231366716?color=7289da&label=chat&logo=discord" alt="Discord"></a></p>
15
+
16
+ <p align="center"><a href="CHANGELOG.md"><img src="https://img.shields.io/badge/Keep%20a%20Changelog-v1.1.0%20adopted-red.svg" alt="Changelog"></a>
17
+ <a href="CODE_OF_CONDUCT.md"><img src="https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg" alt="Contributor Covenant"></a></p>
18
+
19
+ <p align="center">Credible was extracted from <a href="https://github.com/thombruce/helvellyn">thombruce/helvellyn</a> and is still a work in progress. The goal is to provide JWT and API token based authentication using <a href="https://github.com/wardencommunity/warden/">Warden</a> for Rails API applications.</p>
16
20
 
17
21
  ## Installation
18
22
 
@@ -73,6 +77,20 @@ class Session < ApplicationRecord
73
77
  end
74
78
  ```
75
79
 
80
+ ## Calling the API
81
+
82
+ To sign up and create a new user, send params in the form `{ user: { email: 'email@example.com', password: 'Pa$$word123!' } }` to `auth/signup` as a `POST` request. On success, this will return `{ access_token: '...', refresh_token: '...' }` that can be used to persist the user's session.
83
+
84
+ To sign in a user, send params in the form `{ session: { login: 'email@example.com', password: 'Pa$$word123!' } }` to `auth/login` as a `POST` request. On success, this will return `{ access_token: '...', refresh_token: '...' }` that can be used to persist the user's session.
85
+
86
+ To sign out a user, send an empty request to `auth/signout` as a `DELETE` request. On success, this will return no content. It will destroy the session in the database and invalidate any existing tokens. Note: If you are using the access token to authenticate the user on a separate server, it will still remain valid until its expiry.
87
+
88
+ To refresh an access token, send params in the form `{ refresh_token: '...' }` to `auth/refresh` as a `POST` request. On success, this will return `{ access_token: '...', refresh_token: '...' }` that can be used to continue the user's session. Note: This will also invalidate the refresh token used to renew the tokens, and provide a new refresh token for next use.
89
+
90
+ > **Be careful**
91
+ >
92
+ > Credible is still a work in progress and these routes will be subject to change. I'm thinking about keeping them around as sort of a "classic" mode, but I feel like more resource-oriented routes are also desirable. For example, I want to add a `/tokens` endpoint in place of the existing `/refresh` enpoint, because _refresh what?_ It's just more semantically correct.
93
+
76
94
  ## Mailers
77
95
 
78
96
  To user Credible's in-built mailers, you must set a hostname for your application. A suitable setting for this in `config/environments/development.rb` might be:
@@ -1,5 +1,7 @@
1
1
  json.extract! session, :id, :created_at, :updated_at
2
2
 
3
- json.jwt session.jwt
3
+ json.access_token session.access_token
4
+ json.jwt session.access_token
5
+ json.refresh_token session.refresh_token
4
6
 
5
7
  # json.url session_url(session, format: :json)
@@ -1,5 +1,10 @@
1
1
  json.extract! user, :id, :email, :created_at, :updated_at
2
2
 
3
- json.jwt @session.jwt if @session
3
+ if @session
4
+ json.access_token @session.access_token
5
+ json.jwt @session.access_token
6
+
7
+ json.refresh_token @session.refresh_token
8
+ end
4
9
 
5
10
  json.url user_url(user, format: :json)
@@ -3,12 +3,13 @@ Rails.application.config.middleware.use Warden::Manager do |config|
3
3
 
4
4
  config.default_scope = :session
5
5
 
6
- config.scope_defaults :session, store: false, strategies: [:jwt, :api_token]
6
+ config.scope_defaults :session, store: false, strategies: [:jwt, :refresh, :api_token]
7
7
  end
8
8
 
9
9
  # TODO: See here for how Devise initializes Warden: https://github.com/heartcombo/devise/blob/715192a7709a4c02127afb067e66230061b82cf2/lib/devise/rails.rb
10
10
  # It's also worth perusing the mention of 'warden' in the Devise repo. Interesting strategies at work.
11
11
 
12
+ # TODO: Change to :access_token, maybe maintain :jwt as alias
12
13
  Warden::Strategies.add(:jwt) do
13
14
  def valid?
14
15
  env['HTTP_AUTHORIZATION']
@@ -25,14 +26,49 @@ Warden::Strategies.add(:jwt) do
25
26
  jwt = header.gsub(pattern, '') if header && header.match(pattern)
26
27
  token =
27
28
  JWT.decode jwt, Rails.application.secrets.secret_key_base, true,
28
- iss: Rails.application.class.module_parent_name, verify_iss: true, algorithm: 'HS256' # [1]
29
- rescue JWT::InvalidIssuerError
29
+ iss: Rails.application.class.module_parent_name,
30
+ verify_iss: true, verify_iat: true, verify_expiration: true,
31
+ algorithm: 'HS256' # [1]
32
+ rescue JWT::InvalidIssuerError, JWT::InvalidIatError, JWT::ExpiredSignature
30
33
  fail!('Could not authenticate')
31
34
  end
32
35
 
33
36
  session = ::Session.find(token[0]['data']['session_id'])
34
37
  success!(session)
35
- rescue ActiveRecord::RecordNotFound
38
+ rescue ActiveRecord::RecordNotFound
39
+ fail!('Could not authenticate')
40
+ end
41
+
42
+ def store?
43
+ false
44
+ end
45
+ end
46
+
47
+ Warden::Strategies.add(:refresh) do
48
+ def valid?
49
+ # params[:refresh_token]
50
+ env['action_dispatch.request.parameters']['refresh_token'] # https://github.com/wardencommunity/warden/issues/84
51
+ end
52
+
53
+ def env
54
+ request.env
55
+ end
56
+
57
+ def authenticate!
58
+ begin
59
+ jwt = env['action_dispatch.request.parameters']['refresh_token']
60
+ token =
61
+ JWT.decode jwt, Rails.application.secrets.secret_key_base, true,
62
+ iss: Rails.application.class.module_parent_name,
63
+ verify_iss: true, verify_iat: true, verify_expiration: true,
64
+ algorithm: 'HS256' # [1]
65
+ rescue JWT::InvalidIssuerError, JWT::InvalidIatError, JWT::ExpiredSignature
66
+ fail!('Could not authenticate')
67
+ end
68
+
69
+ session = ::Session.find(token[0]['data']['session_id'])
70
+ success!(session)
71
+ rescue # ActiveRecord::RecordNotFound
36
72
  fail!('Could not authenticate')
37
73
  end
38
74
 
data/config/routes.rb CHANGED
@@ -2,12 +2,19 @@ Credible::Engine.routes.draw do
2
2
  scope format: false, defaults: { format: 'json' } do
3
3
  # /auth/login.json
4
4
  post 'login', to: 'sessions#create'
5
+
6
+ # /auth/refresh.json
7
+ post 'refresh', to: 'sessions#refresh'
8
+
5
9
  # /auth/reset_password.json
6
10
  post 'reset_password', to: 'users#reset_password'
11
+
7
12
  # /auth/signup.json
8
13
  post 'signup', to: 'users#create'
14
+
9
15
  # /auth/confirm.json
10
16
  get 'confirm/:confirmation_token', to: 'users#confirm', as: :confirmation
17
+
11
18
  # /auth/signout.json
12
19
  delete 'signout', to: 'sessions#destroy'
13
20
 
@@ -15,6 +22,7 @@ Credible::Engine.routes.draw do
15
22
  scope '/account' do
16
23
  # /auth/account/sessions/*.json
17
24
  resources :sessions, except: [:new, :create, :edit, :update]
25
+
18
26
  # /auth/account/*.json
19
27
  resource :user, path: '', except: [:new, :create]
20
28
  end
@@ -5,7 +5,8 @@ module Credible
5
5
 
6
6
  included do
7
7
  before_action :set_session, only: [:show, :destroy]
8
- skip_before_action :authenticate!, only: [:new, :create, :fail]
8
+ skip_before_action :authenticate!, only: [:new, :create, :refresh, :fail]
9
+ before_action :authenticate_with_refresh_token!, only: [:refresh]
9
10
  # skip_after_action :verify_authorized, only: [:fail]
10
11
  # TODO: Reevaluate authorization without Pundit
11
12
  end
@@ -37,6 +38,19 @@ module Credible
37
38
  end
38
39
  end
39
40
 
41
+ # POST /refresh
42
+ # POST /refresh.json
43
+ def refresh
44
+ @session = ::Session.new(user: current_user)
45
+
46
+ if @session.save
47
+ current_session.destroy
48
+ render :show, status: :created, location: @session
49
+ else
50
+ render json: @session.errors, status: :unprocessable_entity
51
+ end
52
+ end
53
+
40
54
  # DELETE /sessions/1
41
55
  # DELETE /sessions/1.json
42
56
  # DELETE /sessions/current
@@ -60,6 +74,10 @@ module Credible
60
74
  def session_params
61
75
  params.require(:session).permit(:login, :password)
62
76
  end
77
+
78
+ def authenticate_with_refresh_token!
79
+ warden.authenticate!(:refresh)
80
+ end
63
81
  end
64
82
  end
65
83
  end
@@ -9,10 +9,22 @@ module Credible
9
9
 
10
10
  has_secure_token
11
11
 
12
- def jwt
12
+ def access_token
13
13
  payload = {
14
- data: jwt_data,
15
- iss: Rails.application.class.module_parent_name
14
+ data: access_token_data,
15
+ iss: Rails.application.class.module_parent_name,
16
+ iat: Time.now.to_i,
17
+ exp: Time.now.to_i + 4 * 3600
18
+ }
19
+ JWT.encode payload, Rails.application.secrets.secret_key_base, 'HS256' # [1]
20
+ end
21
+
22
+ def refresh_token
23
+ payload = {
24
+ data: refresh_token_data,
25
+ iss: Rails.application.class.module_parent_name,
26
+ iat: Time.now.to_i,
27
+ exp: Time.now.to_i + 14 * 24 * 3600
16
28
  }
17
29
  JWT.encode payload, Rails.application.secrets.secret_key_base, 'HS256' # [1]
18
30
  end
@@ -27,7 +39,7 @@ module Credible
27
39
 
28
40
  private
29
41
 
30
- def jwt_data
42
+ def access_token_data
31
43
  {
32
44
  session_id: id,
33
45
  user_id: user.id,
@@ -37,6 +49,12 @@ module Credible
37
49
  }
38
50
  }
39
51
  end
52
+
53
+ def refresh_token_data
54
+ {
55
+ session_id: id
56
+ }
57
+ end
40
58
  end
41
59
  end
42
60
 
@@ -1,3 +1,3 @@
1
1
  module Credible
2
- VERSION = '0.11.0'
2
+ VERSION = '0.12.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: credible
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thom Bruce
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-23 00:00:00.000000000 Z
11
+ date: 2020-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails