blunt 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +12 -0
- data/README.md +113 -20
- data/TODO.md +4 -4
- data/blunt.gemspec +6 -11
- data/lib/blunt.rb +5 -18
- data/lib/blunt/controller.rb +47 -9
- data/lib/blunt/controller/web.rb +24 -0
- data/lib/blunt/login.rb +17 -0
- data/lib/blunt/signup.rb +16 -0
- data/lib/blunt/token.rb +123 -11
- data/lib/blunt/version.rb +12 -1
- metadata +21 -89
- data/bin/setup +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7dda9fac934e626ea58083ba4ed77bcf97d1f04e
|
4
|
+
data.tar.gz: b80600b6a7b1d798360788c7e3a2e1739e00ce04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3580a3b4252ee6aa22e68de538eccc9807c4d9cbc61e426ff4733102f320dd5db3c3acdd53945b991101af318043c70c9f1708e2b5203eba0bbb89c5dd66554c
|
7
|
+
data.tar.gz: a20e5f4b2ef1644b877c0effa9ad81dd0737099e8fa074615d33a669b72d8a2bebd6937137370b77d47a4b71cb824050e1e52b513cdb695f59c09c81034102c7
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# Blunt
|
2
2
|
|
3
|
-
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/blunt.svg)](https://badge.fury.io/rb/blunt)
|
4
|
+
|
5
|
+
**Blunt** is a token-based authentication library for Hanami. Its goal is to easily implement a single authentication scheme across multiple interfaces, i.e. both web/multi-page and mobile/SPA/API/async.
|
4
6
|
|
5
7
|
## Installation
|
6
8
|
|
@@ -19,44 +21,135 @@ Or:
|
|
19
21
|
gem install blunt
|
20
22
|
```
|
21
23
|
|
22
|
-
##
|
24
|
+
## Setup
|
25
|
+
|
26
|
+
### Environment
|
27
|
+
|
28
|
+
- `ENV['BLUNT_SECRET']` (String)
|
29
|
+
|
30
|
+
### Models
|
31
|
+
|
32
|
+
- `User` entity (+ `UserRepository`)
|
33
|
+
+ `email`
|
34
|
+
+ `password_digest`
|
35
|
+
- `RefreshToken` entity (+ `RefreshTokenRepository`)
|
36
|
+
+ `user_id`
|
37
|
+
+ `valid` (Boolean)
|
38
|
+
+ timestamps
|
39
|
+
|
40
|
+
### Token Handling
|
41
|
+
|
42
|
+
Blunt offers several strategies for token storage. Just include the appropriate module, and Blunt will handle tokens on the back-end - they never need to touch your code!
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
Hanami::Controller.configure do
|
46
|
+
prepare do
|
47
|
+
include Blunt::Controller::__STRATEGY__
|
48
|
+
end
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
#### Cookies
|
53
|
+
|
54
|
+
`Blunt::Controller::Web`
|
55
|
+
|
56
|
+
The ultimate hands-off approach. No setup is required on the front-end. Ideal for multi-page apps.
|
57
|
+
|
58
|
+
TODO: incorporate asynchronous JSON responses
|
59
|
+
|
60
|
+
#### HTTP Headers
|
61
|
+
|
62
|
+
`Blunt::Controller::API`
|
63
|
+
|
64
|
+
TODO: develop this
|
65
|
+
|
66
|
+
Pass the token from the client in a request header: `'HTTP_AUTHORIZATION' => 'Bearer <TOKEN>'`.
|
23
67
|
|
24
|
-
|
68
|
+
#### Custom Strategy
|
69
|
+
|
70
|
+
Define a few methods to govern the access token lifecycle as it gets passed to and from the client. Blunt will use these 3 methods to handle the token. The example below shows a simple implementation for synchronous requests using cookies, but a custom strategy will likely be more complex.
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
module MyCustomStrategy
|
74
|
+
include Blunt::Controller
|
75
|
+
|
76
|
+
private
|
77
|
+
def set_access_token(token)
|
78
|
+
# Login or renew
|
79
|
+
cookies[:access_token] = token
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_access_token
|
83
|
+
# Authenticate
|
84
|
+
cookies[:access_token]
|
85
|
+
end
|
86
|
+
|
87
|
+
def unset_access_token
|
88
|
+
# Logout
|
89
|
+
cookies[:access_token] = nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
```
|
93
|
+
|
94
|
+
## Usage
|
25
95
|
|
26
96
|
### Signup
|
27
97
|
|
28
98
|
```ruby
|
29
|
-
#
|
30
|
-
|
31
|
-
|
99
|
+
# your signup interactor
|
100
|
+
class MySignupClass < Thalamus::Interactor
|
101
|
+
validations do
|
102
|
+
# password length, complexity, etc
|
103
|
+
# ...
|
104
|
+
end
|
105
|
+
def call
|
106
|
+
@user = Blunt.signup(params)
|
107
|
+
end
|
108
|
+
expose :user
|
109
|
+
end
|
110
|
+
# inside your signup controller
|
111
|
+
if user = MySignupClass.call(params).user
|
112
|
+
login!(user)
|
113
|
+
# ...
|
32
114
|
else
|
33
|
-
#
|
115
|
+
# ...
|
34
116
|
end
|
35
117
|
```
|
36
118
|
|
37
|
-
|
119
|
+
`Blunt.signup` compares a `:password` and `:password_confirmation` field. If they match, it creates a new `User` and saves it to the database with a `:password_digest`. If they don't match, it returns nil.
|
120
|
+
|
121
|
+
If you don't use the interactor pattern, You can also use the shortcut `#signup!` directly from your controller. However, you may wish to validate user data and password complexity yourself.
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
# inside your signup controller
|
125
|
+
if signup!(params)
|
126
|
+
# Blunt calls #login! under the hood
|
127
|
+
# ...
|
128
|
+
else
|
129
|
+
# ...
|
130
|
+
end
|
131
|
+
```
|
38
132
|
|
39
133
|
### Login
|
40
134
|
|
41
135
|
```ruby
|
42
136
|
# inside your login controller
|
43
|
-
token =
|
137
|
+
token = login!(params)
|
138
|
+
# ...
|
44
139
|
```
|
45
140
|
|
46
|
-
|
47
|
-
- `attempted` is the unencrypted password attempt as sent by the client.
|
48
|
-
- `claims` is a hash of JWT claims. It _must_ contain a `:sub` key whose value is any unique way to identify the user. You can also send optional JWT claims with the payload, such as `:exp`. Refer to the [ruby-jwt docs](https://github.com/jwt/ruby-jwt) for more information.
|
49
|
-
|
50
|
-
If the passwords match and a `:sub` claim is present, a token will be generated for the claims. If the login attempt fails, the token will be nil. Have your controller return the token to the client and store it somewhere (cookies, local storage, etc).
|
141
|
+
`#login!` finds a user with `:email` and checks that `:password` is correct. Returns an access token if successful, otherwise nil.
|
51
142
|
|
52
143
|
### Request Authentication
|
53
144
|
|
54
|
-
|
55
|
-
|
56
|
-
`include Blunt::Controller` in your controller class. `current_user` will memoize whatever is in `:sub` in the token's payload, or nil if there are any errors.
|
57
|
-
|
58
|
-
If the hash of request headers is not at `request.env`, you will need to override `#_blunt_request_env` to return it. (This works out of the box for Rails and Hanami.)
|
145
|
+
All strategies provide `#current_user` (`User` entity or nil) and `#authenticated?` (Boolean) instance methods to the controller. The token will not be checked until one of these methods is called.
|
59
146
|
|
60
147
|
### Logout
|
61
148
|
|
62
|
-
|
149
|
+
```ruby
|
150
|
+
# inside your logout controller
|
151
|
+
logout!
|
152
|
+
# ...
|
153
|
+
```
|
154
|
+
|
155
|
+
That's it!
|
data/TODO.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# TODO
|
2
2
|
|
3
|
-
-
|
4
|
-
- Defaults for important reserved claims (exp, iss, iat)
|
3
|
+
- Configurable (access & refresh expiry times, etc)
|
5
4
|
- Devise-esque modules (rememberable, confirmable, reset password, etc)
|
6
|
-
-
|
7
|
-
-
|
5
|
+
- Separate credentials from user business concerns, multiple credentials (Omniauth)
|
6
|
+
- Increased security features (user agent checking, etc)
|
7
|
+
- Hard hanami-model dependency, or adapt gem to different stacks?
|
data/blunt.gemspec
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path("../lib", __FILE__)
|
3
|
-
|
3
|
+
$:.unshift(lib) unless $:.include?(lib)
|
4
4
|
require "blunt/version"
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "blunt"
|
8
|
-
spec.version = Blunt
|
8
|
+
spec.version = Blunt.version
|
9
9
|
spec.authors = ["Josh Greenberg"]
|
10
10
|
spec.email = ["joshgreenberg91@gmail.com"]
|
11
11
|
spec.homepage = "https://github.com/joshgreenberg/blunt"
|
@@ -22,14 +22,9 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
24
|
spec.add_development_dependency "bundler", "~> 1.15"
|
25
|
-
spec.add_development_dependency "rake", "~>
|
26
|
-
spec.add_development_dependency "minitest", "~> 5.0"
|
27
|
-
spec.add_development_dependency "minitest-reporters"
|
28
|
-
spec.add_development_dependency "pry"
|
29
|
-
spec.add_development_dependency "guard"
|
30
|
-
spec.add_development_dependency "guard-minitest"
|
31
|
-
spec.add_development_dependency "rake-notes"
|
25
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
32
26
|
|
33
|
-
spec.
|
34
|
-
spec.
|
27
|
+
spec.add_dependency "jwt", "~> 2.0"
|
28
|
+
spec.add_dependency "bcrypt", "3.1.11"
|
29
|
+
spec.add_dependency "hanami-model", "~> 1.0"
|
35
30
|
end
|
data/lib/blunt.rb
CHANGED
@@ -1,23 +1,10 @@
|
|
1
|
-
require "blunt/version"
|
2
|
-
require "blunt/token"
|
3
|
-
require "blunt/controller"
|
4
|
-
|
5
1
|
module Blunt
|
6
|
-
|
7
|
-
|
8
|
-
def new_secret(n = 64)
|
2
|
+
def self.new_secret(n = 64)
|
9
3
|
require 'securerandom'
|
10
4
|
SecureRandom.urlsafe_base64(n)
|
11
5
|
end
|
12
|
-
|
13
|
-
def signup(a, b)
|
14
|
-
require 'bcrypt'
|
15
|
-
BCrypt::Password.create(a) if a == b
|
16
|
-
end
|
17
|
-
|
18
|
-
def login(expected, attempted, claims = {})
|
19
|
-
return unless claims[:sub]
|
20
|
-
require 'bcrypt'
|
21
|
-
Token.issue(claims) if BCrypt::Password.new(expected) == attempted
|
22
|
-
end
|
23
6
|
end
|
7
|
+
|
8
|
+
require 'blunt/signup'
|
9
|
+
require 'blunt/login'
|
10
|
+
require 'blunt/controller'
|
data/lib/blunt/controller.rb
CHANGED
@@ -4,21 +4,59 @@ module Blunt
|
|
4
4
|
module Controller
|
5
5
|
private
|
6
6
|
|
7
|
-
def
|
8
|
-
|
7
|
+
def signup!(params = {})
|
8
|
+
if user = Blunt.signup(params)
|
9
|
+
login!(user)
|
10
|
+
end
|
9
11
|
end
|
10
12
|
|
11
|
-
def
|
12
|
-
|
13
|
+
def login!(params = {})
|
14
|
+
if token = Blunt.login(params)
|
15
|
+
set_access_token(token.to_s)
|
16
|
+
else
|
17
|
+
unset_access_token
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def logout!
|
22
|
+
access_token && access_token.invalidate!
|
23
|
+
@current_user = nil
|
24
|
+
unset_access_token
|
25
|
+
end
|
26
|
+
|
27
|
+
def authenticate!
|
28
|
+
redirect_to '/login' unless authenticated?
|
13
29
|
end
|
14
30
|
|
15
|
-
def
|
16
|
-
|
31
|
+
def authenticated?
|
32
|
+
!!current_user
|
17
33
|
end
|
18
34
|
|
19
|
-
def
|
20
|
-
|
35
|
+
def current_user
|
36
|
+
@current_user ||= begin
|
37
|
+
user = begin
|
38
|
+
if access_token && access_token.valid?
|
39
|
+
UserRepository.new.find(access_token.sub)
|
40
|
+
end
|
41
|
+
rescue
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
set_access_token(access_token.to_s)
|
45
|
+
user
|
46
|
+
end
|
21
47
|
end
|
22
48
|
|
23
|
-
|
49
|
+
def access_token
|
50
|
+
@access_token ||= begin
|
51
|
+
encoded_jwt = get_access_token
|
52
|
+
if encoded_jwt.nil? || encoded_jwt.empty?
|
53
|
+
nil
|
54
|
+
else
|
55
|
+
Token.new(encoded_jwt)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end # module Controller
|
24
60
|
end
|
61
|
+
|
62
|
+
require 'blunt/controller/web'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Blunt
|
2
|
+
module Controller
|
3
|
+
module Web
|
4
|
+
include Controller
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def set_access_token(token)
|
9
|
+
# Login or renew
|
10
|
+
cookies[:access_token] = token
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_access_token
|
14
|
+
# Authenticate
|
15
|
+
cookies[:access_token]
|
16
|
+
end
|
17
|
+
|
18
|
+
def unset_access_token
|
19
|
+
# Logout
|
20
|
+
cookies[:access_token] = nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/blunt/login.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'bcrypt'
|
2
|
+
|
3
|
+
module Blunt
|
4
|
+
def self.login(params = {})
|
5
|
+
user = case params
|
6
|
+
when Hash
|
7
|
+
UserRepository.new.find_by_email(params[:email])
|
8
|
+
else
|
9
|
+
params
|
10
|
+
end
|
11
|
+
if user && BCrypt::Password.new(user.password_digest) == params[:password]
|
12
|
+
Token.create(user.id)
|
13
|
+
else
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/blunt/signup.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'bcrypt'
|
2
|
+
|
3
|
+
module Blunt
|
4
|
+
def self.signup(params = {})
|
5
|
+
password, password_confirmation = params[:password], params[:password_confirmation]
|
6
|
+
if password && password == password_confirmation
|
7
|
+
digest = BCrypt::Password.create(password)
|
8
|
+
params.delete(:password)
|
9
|
+
params.delete(:password_confirmation)
|
10
|
+
params[:password_digest] = digest
|
11
|
+
UserRepository.new.create(params)
|
12
|
+
else
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/blunt/token.rb
CHANGED
@@ -1,25 +1,137 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require 'jwt'
|
2
|
+
require 'openssl'
|
3
|
+
require 'base64'
|
3
4
|
|
4
5
|
module Blunt
|
5
|
-
|
6
|
-
|
6
|
+
class Token
|
7
|
+
ALGORITHM = 'HS256'.freeze
|
8
|
+
EXPIRY_TIME = 3600.freeze
|
7
9
|
|
8
|
-
|
10
|
+
def initialize(token)
|
11
|
+
if token.is_a? String
|
12
|
+
@payload = begin
|
13
|
+
decode(token)
|
14
|
+
rescue
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
else
|
18
|
+
@payload = {
|
19
|
+
sub: token.user_id,
|
20
|
+
jti: encrypt(token.id),
|
21
|
+
exp: token.created_at.to_i + EXPIRY_TIME
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
9
25
|
|
10
|
-
def
|
11
|
-
|
26
|
+
def self.create(sub)
|
27
|
+
new(repo.create(user_id: sub))
|
12
28
|
end
|
13
29
|
|
14
|
-
def
|
15
|
-
|
30
|
+
def inspect
|
31
|
+
"\#<Blunt::Token #{to_s}>"
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
to_h ? encode : nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_h
|
39
|
+
@payload
|
40
|
+
end
|
41
|
+
|
42
|
+
def sub
|
43
|
+
@payload[:sub]
|
44
|
+
end
|
45
|
+
|
46
|
+
def valid?
|
47
|
+
if @payload.nil?
|
48
|
+
# Invalid token format
|
49
|
+
false
|
50
|
+
elsif @payload[:exp].to_i <= Time.now.to_i
|
51
|
+
# Access token expired
|
52
|
+
record = repo.find(jti)
|
53
|
+
if record && record.valid?
|
54
|
+
# Refresh token valid
|
55
|
+
@payload[:exp] = Time.now.to_i + EXPIRY_TIME
|
56
|
+
repo.update(record, {}) if record.respond_to?(:updated_at)
|
57
|
+
@encoded = nil
|
58
|
+
encode
|
59
|
+
else
|
60
|
+
# Refresh token expired
|
61
|
+
false
|
62
|
+
end
|
63
|
+
else
|
64
|
+
# Access token valid
|
65
|
+
true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def invalidate!
|
70
|
+
# Destroy refresh token
|
71
|
+
repo.delete(jti)
|
72
|
+
rescue
|
73
|
+
nil
|
74
|
+
ensure
|
75
|
+
@payload = nil
|
16
76
|
end
|
17
77
|
|
18
78
|
private
|
19
79
|
|
80
|
+
def repo
|
81
|
+
self.class.repo
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.repo
|
85
|
+
@repo ||= RefreshTokenRepository.new
|
86
|
+
end
|
87
|
+
|
88
|
+
def jti
|
89
|
+
decrypt(@payload[:jti])
|
90
|
+
end
|
91
|
+
|
20
92
|
def secret
|
21
|
-
ENV[
|
93
|
+
@secret ||= ENV['BLUNT_SECRET']
|
94
|
+
end
|
95
|
+
|
96
|
+
def encode
|
97
|
+
@encoded ||= JWT::Encode.new(@payload, secret, ALGORITHM, {}).segments
|
22
98
|
end
|
23
99
|
|
24
|
-
|
100
|
+
def decode(token)
|
101
|
+
raise JWT::DecodeError, 'Nil JSON web token' unless token
|
102
|
+
decoder = JWT::Decode.new(token, true)
|
103
|
+
header, payload, signature, signing_input = decoder.decode_segments
|
104
|
+
|
105
|
+
# Verify signature
|
106
|
+
JWT.decode_verify_signature(secret, header, payload, signature, signing_input, algorithm: ALGORITHM)
|
107
|
+
|
108
|
+
# Verify JWT format
|
109
|
+
raise JWT::DecodeError, 'Incorrect number of segments' unless header && payload
|
110
|
+
payload = payload.map{ |k,v| [k.to_sym, v] }.to_h
|
111
|
+
|
112
|
+
# Verify Blunt format
|
113
|
+
raise JWT::DecodeError, 'JWT claims do not meet Blunt specifications' unless payload.key?(:sub) && payload.key?(:exp) && payload.key?(:jti)
|
114
|
+
|
115
|
+
payload
|
116
|
+
end
|
117
|
+
|
118
|
+
# Two-way encryption methods for JTI
|
119
|
+
def encrypt(string)
|
120
|
+
cipher = OpenSSL::Cipher::AES256.new(:CBC)
|
121
|
+
cipher.encrypt
|
122
|
+
cipher.key = Digest::SHA256.digest(secret)
|
123
|
+
cipher.iv = iv = cipher.random_iv
|
124
|
+
encrypted = iv + cipher.update(string.to_s) + cipher.final
|
125
|
+
Base64.encode64(encrypted).gsub(/\s/, '')
|
126
|
+
end
|
127
|
+
|
128
|
+
def decrypt(encrypted)
|
129
|
+
string = Base64.decode64(encrypted)
|
130
|
+
cipher = OpenSSL::Cipher::AES256.new(:CBC)
|
131
|
+
cipher.decrypt
|
132
|
+
cipher.key = Digest::SHA256.digest(secret)
|
133
|
+
cipher.iv = string.slice!(0, 16)
|
134
|
+
cipher.update(string) + cipher.final
|
135
|
+
end
|
136
|
+
end # class Token
|
25
137
|
end
|
data/lib/blunt/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blunt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josh Greenberg
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -30,126 +30,56 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '12.0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '12.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: jwt
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
48
|
-
type: :
|
47
|
+
version: '2.0'
|
48
|
+
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '2.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
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: pry
|
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
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: guard
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: guard-minitest
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: rake-notes
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - ">="
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - ">="
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
125
|
-
- !ruby/object:Gem::Dependency
|
126
|
-
name: jwt
|
56
|
+
name: bcrypt
|
127
57
|
requirement: !ruby/object:Gem::Requirement
|
128
58
|
requirements:
|
129
|
-
- -
|
59
|
+
- - '='
|
130
60
|
- !ruby/object:Gem::Version
|
131
|
-
version:
|
61
|
+
version: 3.1.11
|
132
62
|
type: :runtime
|
133
63
|
prerelease: false
|
134
64
|
version_requirements: !ruby/object:Gem::Requirement
|
135
65
|
requirements:
|
136
|
-
- -
|
66
|
+
- - '='
|
137
67
|
- !ruby/object:Gem::Version
|
138
|
-
version:
|
68
|
+
version: 3.1.11
|
139
69
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
70
|
+
name: hanami-model
|
141
71
|
requirement: !ruby/object:Gem::Requirement
|
142
72
|
requirements:
|
143
|
-
- - "
|
73
|
+
- - "~>"
|
144
74
|
- !ruby/object:Gem::Version
|
145
|
-
version:
|
75
|
+
version: '1.0'
|
146
76
|
type: :runtime
|
147
77
|
prerelease: false
|
148
78
|
version_requirements: !ruby/object:Gem::Requirement
|
149
79
|
requirements:
|
150
|
-
- - "
|
80
|
+
- - "~>"
|
151
81
|
- !ruby/object:Gem::Version
|
152
|
-
version:
|
82
|
+
version: '1.0'
|
153
83
|
description: Token authentication
|
154
84
|
email:
|
155
85
|
- joshgreenberg91@gmail.com
|
@@ -166,10 +96,12 @@ files:
|
|
166
96
|
- Rakefile
|
167
97
|
- TODO.md
|
168
98
|
- bin/console
|
169
|
-
- bin/setup
|
170
99
|
- blunt.gemspec
|
171
100
|
- lib/blunt.rb
|
172
101
|
- lib/blunt/controller.rb
|
102
|
+
- lib/blunt/controller/web.rb
|
103
|
+
- lib/blunt/login.rb
|
104
|
+
- lib/blunt/signup.rb
|
173
105
|
- lib/blunt/token.rb
|
174
106
|
- lib/blunt/version.rb
|
175
107
|
homepage: https://github.com/joshgreenberg/blunt
|