lp_token_auth 0.3.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 58b7281e6182492224419d1744647bb269908e95
4
+ data.tar.gz: 9e607f2f76660a71194cae75ff2a936683db0a56
5
+ SHA512:
6
+ metadata.gz: da0e5bfba21a09d402e50abee20ea107f679e66c48439a77e370bb17640823a0f02e5687198057f1cd647a9a87e06c38ebfd82ed3ee7d16fc966f8d4bf119b32
7
+ data.tar.gz: b4a8ecd8307b0d494f18c5a9a1032beda00fb5b14a68a5a5edbaddb75fa88386913a5a5520499250d2e09be21c8c0601b001c79c51e850bc89b9c73b0013307b
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ lp_token_auth-*.gem
2
+ doc
3
+ .yardoc
4
+ coverage
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.3.0
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+
3
+ addons:
4
+ code_climate:
5
+ repo_token: e57b3b36c781473fb047165641730c2653195939bee2d982683d330eab2e9892
6
+
7
+ after_success:
8
+ - bundle exec codeclimate-test-reporter
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ --markup markdown
2
+ --exclude /generators
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group :test do
4
+ gem 'simplecov'
5
+ gem 'codeclimate-test-reporter', '~> 1.0.0'
6
+ end
7
+
8
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,34 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ lp_token_auth (0.3.0)
5
+ jwt (>= 1.5.6)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ codeclimate-test-reporter (1.0.5)
11
+ simplecov
12
+ docile (1.1.5)
13
+ json (2.0.3)
14
+ jwt (1.5.6)
15
+ minitest (5.10.2)
16
+ rake (10.5.0)
17
+ simplecov (0.13.0)
18
+ docile (~> 1.1.0)
19
+ json (>= 1.8, < 3)
20
+ simplecov-html (~> 0.10.0)
21
+ simplecov-html (0.10.0)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ codeclimate-test-reporter (~> 1.0.0)
28
+ lp_token_auth!
29
+ minitest (~> 5.10, >= 5.10.1)
30
+ rake (~> 10.4, >= 10.4.2)
31
+ simplecov
32
+
33
+ BUNDLED WITH
34
+ 1.16.1
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 TODO: Write your name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,93 @@
1
+ [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://www.rubydoc.info/github/LaunchPadLab/lp_token_auth)
2
+ [![Build Status](https://travis-ci.org/LaunchPadLab/lp_token_auth.svg?branch=master)](https://travis-ci.org/LaunchPadLab/lp_token_auth)
3
+ [![Test Coverage](https://codeclimate.com/repos/593aabffc759c90269001912/badges/1e40a4f9bc94a46fc508/coverage.svg)](https://codeclimate.com/repos/593aabffc759c90269001912/coverage)
4
+ [![Code Climate](https://codeclimate.com/repos/593aabffc759c90269001912/badges/1e40a4f9bc94a46fc508/gpa.svg)](https://codeclimate.com/repos/593aabffc759c90269001912/feed)
5
+
6
+ # LP Token Auth
7
+ Simple token authentication logic with JWTs for Rails apps. No baked in routing, just the barebones logic you need to implement token authentication with JWTs.
8
+
9
+ * [Installation](#installation)
10
+ * [Usage](#usage)
11
+ * [Examples](#examples)
12
+
13
+ ## Installation
14
+ Add this line to your application's Gemfile:
15
+
16
+ `gem 'lp_token_auth'`
17
+
18
+ And then execute:
19
+
20
+ `$ bundle`
21
+
22
+ Or install it yourself as:
23
+
24
+ `$ gem install lp_token_auth`
25
+
26
+ ## Usage
27
+ 1. Run `bundle exec rails generate lp_token_auth:install` to generate an initializer at `../config/initalizers/lp_token_auth.rb`. See the initializer for more details about what is configurable.
28
+ 2. In the most senior controller that you want to authenticate, add `include LpTokenAuth::Controller`. This gives you 4 methods that are available in this and all child controllers:
29
+ + `login(user)` - Given a valid user, this will generate a JWT and return it. The token should be sent to the client and passed in the 'Authorization' header in all subsequent requests to the server.
30
+ + `authenticate_request!` - This is a `before_action` to use in your controllers that will extract the token from the header and authenticate it before proceeding. If the resource class that you're using is not the default `User`, you may override the `authenticate_request!` method by creating a custom `before_action`, in which you may pass in the resource class name.
31
+
32
+ ```
33
+ class AuthenticationController < ApplicationController
34
+ include LpTokenAuth::Controller
35
+
36
+ before_action :authenticate_request
37
+
38
+ protected
39
+
40
+ def authenticate_request
41
+ authenticate_request!('AdminUser')
42
+ end
43
+ end
44
+ ```
45
+ + `authenticate!(token)` - This is called by `authenticate_request!` but is available to use if you ever need to manually authenticate a token.
46
+ + `current_user` - This returns the current user identified by `authenticate!`. It is available after logging in the user or authenticating.
47
+ 3. All errors will return an instance of `LpTokenAuth::Error`
48
+
49
+ ## Examples
50
+ ### Controller
51
+ ```
52
+ class AuthenticatingController < ApplicationController
53
+ include LpTokenAuth::Controller
54
+
55
+ before_action :authenticate_request!
56
+
57
+ rescue_from LpTokenAuth::Error, with: :unauthorized
58
+
59
+ protected
60
+
61
+ def unauthorized(error)
62
+ render json: { data: error.message }, status: :unauthorized
63
+ end
64
+ end
65
+ ```
66
+
67
+ ### Api Request
68
+ ```
69
+ // Using fetch api
70
+ const jwt = '...'
71
+ fetch('localhost:3000/authenticated-route', {
72
+ headers: {
73
+ 'Authorization': `Bearer ${jwt}`
74
+ ...
75
+ }
76
+ ...
77
+ })
78
+ ```
79
+
80
+ ## Development
81
+ + `git clone git@github.com:LaunchPadLab/lp_token_auth.git`
82
+ + `bundle install`
83
+
84
+ ### Testing
85
+ + Run tests with `rake`
86
+
87
+ ## FAQ
88
+
89
+ ### Can I use this without ActiveRecord?
90
+
91
+ Almost! There is a slight dependence on the ActiveRecord method `find`, which is used in order to decode a token based on the resource's `id`. The current workaround is to make sure the resource class you're using implements `find`, and has either a column `id` or implements a method called `id`.
92
+
93
+ ## Authenticate away!
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ end
6
+
7
+ desc "Run tests"
8
+ task :default => :test
@@ -0,0 +1,19 @@
1
+ require 'securerandom'
2
+
3
+ module LpTokenAuth
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ source_root File.expand_path('../templates', __FILE__)
7
+
8
+ desc 'Creates a LpTokenAuth initializer in your application.'
9
+
10
+ def create_initializer
11
+ template 'initializer.rb.erb', 'config/initializers/lp_token_auth.rb'
12
+ end
13
+
14
+ def secret_key
15
+ SecureRandom.hex(64)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ LpTokenAuth.config do |config|
2
+
3
+ # The secret used when encrypting the JWT
4
+ #
5
+ config.secret = '<%= secret_key %>'
6
+
7
+ # The number of hours the token is active
8
+ # default: 7 * 24
9
+ #
10
+ config.expires = 7 * 24
11
+
12
+ # The encryption algorithm to use
13
+ # default: HS512
14
+ #
15
+ config.algorithm = 'HS512'
16
+
17
+ # Where to include the token in the response, must be an array, options are
18
+ # :cookie, :header
19
+ # default: [:cookie]
20
+ #
21
+ config.token_transport = [:cookie]
22
+ end
@@ -0,0 +1,16 @@
1
+ module LpTokenAuth
2
+ def self.config
3
+ @config ||= LpTokenAuth::Config.new
4
+ if block_given?
5
+ yield @config
6
+ else
7
+ @config
8
+ end
9
+ end
10
+ end
11
+
12
+ require 'lp_token_auth/config'
13
+ require 'lp_token_auth/error'
14
+ require 'lp_token_auth/core'
15
+ require 'lp_token_auth/controller'
16
+ require 'lp_token_auth/version'
@@ -0,0 +1,31 @@
1
+ require 'lp_token_auth/error'
2
+
3
+ module LpTokenAuth
4
+ # `LpTokenAuth::Config` manages the configuration options for the token.
5
+ # These can be set with the initializer provided with the generator.
6
+ class Config
7
+ # Creates virtual attributes for configuration options:
8
+ # * `algorithm` is a string corresponding to token encryption algorithm to use
9
+ # * `expires` is an integer corresponding to the number of hours that the token is active
10
+ # * `secret` is a string corresponding to the secret key used when encrypting the token
11
+ # * `token_transport` is a string indicating where to include the token in the HTTP response
12
+ attr_accessor :algorithm, :expires, :secret, :token_transport
13
+
14
+ # Provides default values to token options
15
+ DEFAULT_VALUES = {
16
+ algorithm: 'HS512',
17
+ expires: (7 * 24),
18
+ token_transport: [:cookie],
19
+ }
20
+
21
+ # Retrieves value for token option, either as set by the application, or the default
22
+ # @param [Symbol] key the token option name
23
+ # @raise [LpTokenAuth::Error] if the option has not been set by the application and a default value does not exist
24
+ # @return [String,Integer] the value of the token option
25
+ def get_option(key)
26
+ option = send(key) || DEFAULT_VALUES[key]
27
+ raise LpTokenAuth::Error "Missing config option value: #{ key }" unless option
28
+ option
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,113 @@
1
+ require 'lp_token_auth/core'
2
+ require 'json'
3
+
4
+ module LpTokenAuth
5
+ # `LpTokenAuth::Controller` contains the primary functionality of the `LpTokenAuth` gem.
6
+ # The `Controller` module contains the logic around setting and clearing tokens for
7
+ # a resource, as well as authenticating requests with a token.
8
+ module Controller
9
+
10
+ # Creates and sets a JWT token for a resource
11
+ # @param [Object] user the resource
12
+ # @param [String] context any contextual information necessary for authentication
13
+ # @return [String] encoded token
14
+ def login(user, context='')
15
+ token = LpTokenAuth.issue_token(user.id)
16
+ set_current_user user
17
+ set_token token, context
18
+ token
19
+ end
20
+
21
+ # Deletes the `lp_auth` key from the `cookies` hash
22
+ # @return [nil]
23
+ def logout
24
+ clear_token
25
+ end
26
+
27
+ # Retrieves and authenticates the token for the given resource
28
+ # @param [Symbol, String] resource the symbolized or stringified class of the resource
29
+ # @raise [LpTokenAuth::Error] if the token is invalid or otherwise unable to be decoded
30
+ # @return [Object] @current_user
31
+ def authenticate_request!(resource=:user)
32
+ token = get_token
33
+ authenticate_token! token, resource
34
+ end
35
+
36
+ # Decodes the token, and finds and sets the current user
37
+ # @param [String] token the token object
38
+ # @param [Symbol, String] resource the symbolized or stringified class of the resource
39
+ # @raise [LpTokenAuth::Error] if the token is invalid or otherwise unable to decoded
40
+ # @return [Object] @current_user
41
+ def authenticate_token!(token, resource=:user)
42
+ begin
43
+ decoded = LpTokenAuth.decode!(token)
44
+ @current_user = find_lp_resource(resource, decoded)
45
+ rescue LpTokenAuth::Error => error
46
+ logout
47
+ raise error
48
+ rescue => error
49
+ logout
50
+ raise LpTokenAuth::Error, error
51
+ end
52
+ end
53
+
54
+ # Helper method to retrieve the current user
55
+ # @return [Object] @current_user
56
+ def current_user
57
+ @current_user
58
+ end
59
+
60
+ private
61
+
62
+ def set_current_user(user)
63
+ @current_user = user
64
+ end
65
+
66
+ def set_token(token, context)
67
+ lp_auth_cookie = { token: token, context: context }.to_json
68
+ cookies[:lp_auth] = lp_auth_cookie if has_transport?(:cookie)
69
+ response.headers['X-LP-AUTH'] = lp_auth_cookie if has_transport?(:header)
70
+ end
71
+
72
+ def clear_token
73
+ cookies.delete :lp_auth if has_transport?(:cookie)
74
+ end
75
+
76
+ def get_token
77
+ [cookie_token, header_token].compact.first
78
+ end
79
+
80
+ def cookie_token
81
+ return nil unless has_transport?(:cookie)
82
+ parse_token(cookies[:lp_auth])
83
+ end
84
+
85
+ def header_token
86
+ return nil unless has_transport?(:header)
87
+ parse_token(fetch_header_auth)
88
+ end
89
+
90
+ def fetch_header_auth
91
+ request.headers.fetch('Authorization', '').split(' ').last
92
+ end
93
+
94
+ def parse_token(token_path)
95
+ return nil unless token_path
96
+ begin
97
+ parsed = JSON.parse(token_path)
98
+ parsed.fetch('token', nil)
99
+ rescue JSON::ParserError
100
+ token_path
101
+ end
102
+ end
103
+
104
+ def has_transport?(type)
105
+ LpTokenAuth.config.get_option(:token_transport).include?(type)
106
+ end
107
+
108
+ def find_lp_resource(resource, decoded)
109
+ klass = resource.to_s.classify.constantize
110
+ klass.find(decoded['id'])
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,60 @@
1
+ require 'jwt'
2
+ require 'lp_token_auth/error'
3
+
4
+ module LpTokenAuth
5
+ # The `LpTokenAuth` class performs all of the logic of encoding and decoding JWT tokens,
6
+ # and raises appropriate error messages if any errors occur.
7
+ class << self
8
+
9
+ # Encodes the JWT token with the payload
10
+ # @param [Integer, String] id the identifier of the resource
11
+ # @param [Symbol=>String] payload keyword arguments required to create the token
12
+ # @raise [LpTokenAuth::Error] if the `id` is not a `String` or `Integer`
13
+ # @return [String] encoded token
14
+ def issue_token(id, **payload)
15
+
16
+ check_id!(id)
17
+
18
+ payload[:id] = id
19
+
20
+ unless payload.has_key? :exp
21
+ payload[:exp] = (Time.now + LpTokenAuth.config.get_option(:expires) * 60 * 60).to_i
22
+ end
23
+
24
+ JWT.encode(
25
+ payload,
26
+ LpTokenAuth.config.get_option(:secret),
27
+ LpTokenAuth.config.get_option(:algorithm)
28
+ )
29
+ end
30
+
31
+ # Decodes the JWT token
32
+ # @param [String] token the token to decode
33
+ # @raise [LpTokenAuth::Error] if the token is expired, or if any errors occur during decoding
34
+ # @return [Array] decoded token
35
+ def decode!(token)
36
+ begin
37
+ JWT.decode(
38
+ token,
39
+ LpTokenAuth.config.get_option(:secret),
40
+ true,
41
+ algorithm: LpTokenAuth.config.get_option(:algorithm)
42
+ ).first
43
+ rescue JWT::ExpiredSignature => msg
44
+ raise LpTokenAuth::Error, msg
45
+ rescue StandardError => msg
46
+ raise LpTokenAuth::Error, msg
47
+ end
48
+ end
49
+
50
+ # Determines if the `id` provided is either a `String` or an `Integer`
51
+ # @param [Integer, String] id the identifier of the resource
52
+ # @raise [LpTokenAuth::Error] if the `id` is not a `String` or `Integer`
53
+ # @return [nil]
54
+ def check_id!(id)
55
+ unless id.is_a?(String) || id.is_a?(Integer)
56
+ raise LpTokenAuth::Error, "id must be a string or integer, you provided #{id}"
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,4 @@
1
+ module LpTokenAuth
2
+ # The base class for all errors raised by LpTokenAuth
3
+ class Error < StandardError; end
4
+ end
@@ -0,0 +1,4 @@
1
+ module LpTokenAuth
2
+ # Current version of LpTokenAuth
3
+ VERSION = '0.3.0'.freeze
4
+ end
@@ -0,0 +1,21 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'lp_token_auth/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'lp_token_auth'
7
+ s.version = LpTokenAuth::VERSION
8
+ s.date = '2017-02-03'
9
+ s.summary = 'Auth!'
10
+ s.description = 'Simple token authentication'
11
+ s.authors = ['Dave Corwin']
12
+ s.email = 'dave@launchpadlab.com'
13
+ s.homepage = 'https://github.com/launchpadlab/lp_token_auth'
14
+ s.license = 'MIT'
15
+ s.required_ruby_version = '>= 2.3.0'
16
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+
18
+ s.add_dependency 'jwt', '>= 1.5.6'
19
+ s.add_development_dependency 'rake', '~> 10.4', '>= 10.4.2'
20
+ s.add_development_dependency 'minitest', '~> 5.10', '>= 5.10.1'
21
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lp_token_auth
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Dave Corwin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-02-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jwt
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.5.6
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.5.6
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.4'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 10.4.2
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '10.4'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 10.4.2
47
+ - !ruby/object:Gem::Dependency
48
+ name: minitest
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '5.10'
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 5.10.1
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '5.10'
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 5.10.1
67
+ description: Simple token authentication
68
+ email: dave@launchpadlab.com
69
+ executables: []
70
+ extensions: []
71
+ extra_rdoc_files: []
72
+ files:
73
+ - ".gitignore"
74
+ - ".ruby-version"
75
+ - ".travis.yml"
76
+ - ".yardopts"
77
+ - Gemfile
78
+ - Gemfile.lock
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - lib/generators/lp_token_auth/install_generator.rb
83
+ - lib/generators/lp_token_auth/templates/initializer.rb.erb
84
+ - lib/lp_token_auth.rb
85
+ - lib/lp_token_auth/config.rb
86
+ - lib/lp_token_auth/controller.rb
87
+ - lib/lp_token_auth/core.rb
88
+ - lib/lp_token_auth/error.rb
89
+ - lib/lp_token_auth/version.rb
90
+ - lp_token_auth.gemspec
91
+ homepage: https://github.com/launchpadlab/lp_token_auth
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 2.3.0
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.5.1
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Auth!
115
+ test_files: []