credible 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
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