seraph-grape 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/.travis.yml +3 -3
- data/README.md +51 -1
- data/lib/seraph-grape.rb +1 -0
- data/lib/seraph/grape.rb +3 -0
- data/lib/seraph/grape/authenticator.rb +33 -0
- data/lib/seraph/grape/configuration.rb +6 -3
- data/lib/seraph/grape/helpers.rb +23 -0
- data/lib/seraph/grape/jwt.rb +26 -0
- data/lib/seraph/grape/version.rb +1 -1
- data/seraph-grape.gemspec +5 -3
- data/spec/seraph/grape/authenticator_spec.rb +24 -0
- data/spec/seraph/grape/helpers_spec.rb +66 -0
- data/spec/seraph/grape/jwt_spec.rb +29 -0
- data/spec/spec_helper.rb +7 -1
- data/spec/support/dummy_api.rb +38 -0
- data/spec/support/dummy_user.rb +1 -0
- metadata +47 -7
- data/ChangeLog.md +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 03a4b653da5a18ec6b73dbf456a85a02c28b686d
|
4
|
+
data.tar.gz: 68b5a3a130382c8cefb255661d557100a4583f1c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e50ec8975c0ad5511945c00027bcb881f7a5974c6df9a03c7aa486ecf127aa52bb18410d112c4b8c082935d2d6b17f3f9de3af09226a516d8a4fd3ff3c62b04
|
7
|
+
data.tar.gz: 17871012d507a09b41ecdad49061f3aa21e7a1b7b1daa539ee0dabbe5222b7488a2229341fdca406aef3ea2881dafd92de6b15500768b86b34a39fe943ad4001
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
# Seraph Grape integration
|
2
2
|
[![Build Status](https://secure.travis-ci.org/Szeliga/seraph-grape.svg?branch=master)](https://travis-ci.org/Szeliga/seraph-grape)
|
3
|
+
[![Code Climate](https://codeclimate.com/github/Szeliga/seraph-grape/badges/gpa.svg)](https://codeclimate.com/github/Szeliga/seraph-grape)
|
4
|
+
[![Test Coverage](https://codeclimate.com/github/Szeliga/seraph-grape/badges/coverage.svg)](https://codeclimate.com/github/Szeliga/seraph-grape/coverage)
|
3
5
|
|
4
|
-
[Seraph](https://github.com/Szeliga/seraph) helpers for Grape.
|
6
|
+
[Seraph](https://github.com/Szeliga/seraph) helpers for Grape. Provides several simple helpers for implementing a JWT token authentication.
|
7
|
+
|
8
|
+
A sample usage in Grape can be found in [DummyApi Grape Endpoint](spec/support/dummy_api.rb).
|
9
|
+
|
10
|
+
Just like Seraph, this integration doesn't make any assumptions about your stuck. It does however make a small assumption about the authenticated User resource. It requires that this resource responds to two messages - `id` and `encrypted_password`. This is required for authentications and generating of JWT tokens. This resource can be anything from a `Struct`, `OpenStruct` to an `ActiveRecord` model.
|
11
|
+
|
12
|
+
For more information on what Seraph offers, visit the [projects repo](https://github.com/Szeliga/seraph).
|
5
13
|
|
6
14
|
## Installation
|
7
15
|
|
@@ -15,6 +23,48 @@ gem 'seraph-grape'
|
|
15
23
|
```
|
16
24
|
inside your `Gemfile`
|
17
25
|
|
26
|
+
## Configuration
|
27
|
+
|
28
|
+
In addition to the configuration fields that [Seraph](https://github.com/Szeliga/seraph#configuration) provides, the Seraph Grape integration gem adds another option called `api_secret`. This option is used by the JWT library as a secret passed to the hashing algorithm (HS256) to encode the token. **It is strongly recommended that this is set!**
|
29
|
+
|
30
|
+
``` ruby
|
31
|
+
Seraph.configure do |config|
|
32
|
+
config.api_secret = 'GENERATED-SECRET'
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
The secret can be generated in the same manner, the pepper is generated in [Seraph](https://github.com/Szeliga/seraph#configuration).
|
37
|
+
|
38
|
+
## What do you get?
|
39
|
+
|
40
|
+
The library provides several things.
|
41
|
+
|
42
|
+
### Grape helpers
|
43
|
+
|
44
|
+
1. `authenticate!` - checks the `Authorization` header of the incoming request for a JWT token, if it finds a non-emtpy string in the header, it attempts to decode the token. If the decoding fails, a 401 error is returned instead.
|
45
|
+
2. `auth_info` - contains the `user_id` to whom the token was issued. Returns a Hash of the form `{ user_id: 1 }`.
|
46
|
+
3. `sign_in(user, password)` - takes a user resource (previously found by the e-mail, or other login information, posted to the sign in endpoint), and a plaintext password (also provided in the information posted to the sign in endpoint). Checks if the plaintext password matches the encrypted one in the `user` resource, using [Seraph's Password Comparator class](https://github.com/Szeliga/seraph#comparing-a-provided-password-with-the-encrypted-one). If the passwords match, it returns a JWT token for that `user` (using it's `id` method). If they do not, it raises a 401 error.
|
47
|
+
|
48
|
+
### Authenticator
|
49
|
+
|
50
|
+
The integration offers also `Seraph::Grape::Authenticator`. The `sign_in` Grape helper delegates to this class in order to do the authentication, as described above. You can invoke the class like this:
|
51
|
+
|
52
|
+
``` ruby
|
53
|
+
Seraph::Grape::Authenticator.call(user, password)
|
54
|
+
```
|
55
|
+
|
56
|
+
As stated above, if the passwords match, it returns a JWT token for the passed in `user`. If they do not, false is returned instead.
|
57
|
+
|
58
|
+
**Note** - this class will be probably moved to Seraph core in the near future, as it isn't grape-specific and could be used in other setups (`rails-api` for example).
|
59
|
+
|
60
|
+
## Roadmap
|
61
|
+
|
62
|
+
### Version 1.0.0
|
63
|
+
* move out `Seraph::Grape::Authenticator` to the core gem
|
64
|
+
* expose basic JWT options via configuration - algorithm, etc.
|
65
|
+
* implement JWT token [Expiration Time Claim](https://github.com/jwt/ruby-jwt#expiration-time-claim)
|
66
|
+
* implement JWT [Subject Claim](https://github.com/jwt/ruby-jwt#subject-claim) alongside subject verification option for decoding
|
67
|
+
|
18
68
|
## Copyright
|
19
69
|
|
20
70
|
Copyright (c) 2016 Szymon Szeliga
|
data/lib/seraph-grape.rb
CHANGED
data/lib/seraph/grape.rb
CHANGED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Seraph
|
2
|
+
module Grape
|
3
|
+
class Authenticator
|
4
|
+
private_class_method :new
|
5
|
+
|
6
|
+
def self.call(user, password)
|
7
|
+
new(user, password).call
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(user, password)
|
11
|
+
@user = user
|
12
|
+
@password = password
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
return jwt_token if password_valid?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :user, :password
|
23
|
+
|
24
|
+
def jwt_token
|
25
|
+
Seraph::Grape::JWT.encode(user_id: user.id)
|
26
|
+
end
|
27
|
+
|
28
|
+
def password_valid?
|
29
|
+
Seraph::PasswordComparator.call(user.encrypted_password, password)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Seraph
|
2
|
+
module Grape
|
3
|
+
module Helpers
|
4
|
+
def authenticate!
|
5
|
+
unauthorized! if auth_info.nil?
|
6
|
+
end
|
7
|
+
|
8
|
+
def auth_info
|
9
|
+
@auth_info ||= Seraph::Grape::JWT.decode(headers['Authorization'])
|
10
|
+
end
|
11
|
+
|
12
|
+
def sign_in(user, password)
|
13
|
+
result = Seraph::Grape::Authenticator.call(user, password)
|
14
|
+
unauthorized! unless result
|
15
|
+
result
|
16
|
+
end
|
17
|
+
|
18
|
+
def unauthorized!
|
19
|
+
error!('Unauthorized', 401)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'jwt'
|
2
|
+
|
3
|
+
module Seraph
|
4
|
+
module Grape
|
5
|
+
module JWT
|
6
|
+
def encode(payload)
|
7
|
+
::JWT.encode(payload, secret)
|
8
|
+
end
|
9
|
+
module_function :encode
|
10
|
+
|
11
|
+
def decode(token)
|
12
|
+
::JWT.decode(token, secret).first
|
13
|
+
rescue
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
module_function :decode
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def secret
|
21
|
+
String(Seraph.configuration.api_secret)
|
22
|
+
end
|
23
|
+
module_function :secret
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/seraph/grape/version.rb
CHANGED
data/seraph-grape.gemspec
CHANGED
@@ -8,8 +8,8 @@ require 'English'
|
|
8
8
|
Gem::Specification.new do |gem|
|
9
9
|
gem.name = 'seraph-grape'
|
10
10
|
gem.version = Seraph::Grape::VERSION
|
11
|
-
gem.summary = ''
|
12
|
-
gem.description = ''
|
11
|
+
gem.summary = 'Seraph helpers for Grape'
|
12
|
+
gem.description = 'Integrate Seraph with your Grape API'
|
13
13
|
gem.license = 'MIT'
|
14
14
|
gem.authors = ['Szymon Szeliga']
|
15
15
|
gem.email = 'szeliga.szymon@gmail.com'
|
@@ -32,7 +32,9 @@ Gem::Specification.new do |gem|
|
|
32
32
|
|
33
33
|
gem.add_runtime_dependency 'grape', '~> 0.16'
|
34
34
|
gem.add_runtime_dependency 'seraph', '~> 0.0'
|
35
|
-
gem.
|
35
|
+
gem.add_runtime_dependency 'jwt', '~> 1.5'
|
36
|
+
gem.add_development_dependency 'rake', '~> 11.2'
|
37
|
+
gem.add_development_dependency 'rack-test', '~> 0.6'
|
36
38
|
gem.add_development_dependency 'rspec', '~> 3.0'
|
37
39
|
gem.add_development_dependency 'rubygems-tasks', '~> 0.2'
|
38
40
|
gem.add_development_dependency 'fuubar', '~> 2.0'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Seraph::Grape::Authenticator do
|
4
|
+
describe '#call' do
|
5
|
+
subject(:authenticator) { described_class.call(user, password) }
|
6
|
+
let(:user) do
|
7
|
+
DummyUser.new(1, Seraph::PasswordEncryptor.call('foobar12'))
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'when password is valid' do
|
11
|
+
let(:password) { 'foobar12' }
|
12
|
+
|
13
|
+
it 'returns a JWT token with the user id in the payload' do
|
14
|
+
expect(authenticator).to eq Seraph::Grape::JWT.encode(user_id: user.id)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when password is invalid' do
|
19
|
+
let(:password) { 'invalid_password' }
|
20
|
+
|
21
|
+
it { is_expected.to be_falsey }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Seraph::Grape::Helpers do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def app
|
7
|
+
DummyApi.new
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#authenticate!' do
|
11
|
+
before do
|
12
|
+
Seraph.configure do |config|
|
13
|
+
config.api_secret = 'secret'
|
14
|
+
end
|
15
|
+
header('Authorization', jwt)
|
16
|
+
end
|
17
|
+
let(:user) { DummyUser.new(1, '123') }
|
18
|
+
|
19
|
+
context 'when authentication is successful' do
|
20
|
+
let(:jwt) { Seraph::Grape::JWT.encode(user_id: user.id) }
|
21
|
+
|
22
|
+
it 'returns a 200 response' do
|
23
|
+
get '/dummy_api/private'
|
24
|
+
expect(last_response.status).to eq(200)
|
25
|
+
expect(JSON.parse(last_response.body)).to eq 'status' => 'authenticated'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'assigns the payload decoded from the Authorization header' do
|
29
|
+
get '/dummy_api/private_info'
|
30
|
+
expect(last_response.status).to eq(200)
|
31
|
+
expect(JSON.parse(last_response.body)).to eq 'user_id' => user.id
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when authentication is unsuccessful' do
|
36
|
+
let(:jwt) { '' }
|
37
|
+
|
38
|
+
it 'returns a 401 response' do
|
39
|
+
get '/dummy_api/private'
|
40
|
+
expect(last_response.status).to eq(401)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#sign_in' do
|
46
|
+
let(:user) { DummyUser.new(1, Seraph::PasswordEncryptor.call('foobar12')) }
|
47
|
+
|
48
|
+
context 'when valid credentials are passed' do
|
49
|
+
it 'returns the JWT token' do
|
50
|
+
post '/dummy_api/sign_in', email: 'dummyuser@gmail.com', password: 'foobar12'
|
51
|
+
|
52
|
+
expect(last_response.status).to eq(200)
|
53
|
+
expect(JSON.parse(last_response.body)).to eq(
|
54
|
+
'jwt' => Seraph::Grape::JWT.encode(user_id: user.id)
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'when invalid credentials are passed' do
|
60
|
+
it 'returns status 401' do
|
61
|
+
post '/dummy_api/sign_in', email: 'dummyuser@gmail.com', password: 'invalid'
|
62
|
+
expect(last_response.status).to eq(401)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Seraph::Grape::JWT do
|
4
|
+
describe '#encode' do
|
5
|
+
let(:payload) { {} }
|
6
|
+
subject(:token) { described_class.encode(payload) }
|
7
|
+
|
8
|
+
context 'when api_secret configuration option is set' do
|
9
|
+
let(:api_secret) { 'secret' }
|
10
|
+
before do
|
11
|
+
Seraph.configure do |config|
|
12
|
+
config.api_secret = api_secret
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns an encoded JWT token' do
|
17
|
+
expect(token).to eq JWT.encode(payload, api_secret)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when api_secret configuration option is not set' do
|
22
|
+
let(:api_secret) { nil }
|
23
|
+
|
24
|
+
it 'returns an encoded JWT token' do
|
25
|
+
expect(token).to eq JWT.encode(payload, '')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
require 'pry'
|
1
2
|
require 'rspec'
|
2
|
-
require 'seraph/grape/version'
|
3
3
|
require 'codeclimate-test-reporter'
|
4
|
+
require 'seraph'
|
5
|
+
require 'seraph/grape'
|
4
6
|
|
5
7
|
Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
|
6
8
|
|
@@ -15,6 +17,10 @@ RSpec.configure do |config|
|
|
15
17
|
mocks.verify_partial_doubles = true
|
16
18
|
end
|
17
19
|
|
20
|
+
config.before(:each) do
|
21
|
+
Seraph.configuration.reset
|
22
|
+
end
|
23
|
+
|
18
24
|
config.order = :random
|
19
25
|
Kernel.srand config.seed
|
20
26
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rack/test'
|
2
|
+
require 'grape'
|
3
|
+
require 'seraph/grape/helpers'
|
4
|
+
|
5
|
+
class DummyApi < Grape::API
|
6
|
+
helpers Seraph::Grape::Helpers
|
7
|
+
prefix :dummy_api
|
8
|
+
format :json
|
9
|
+
|
10
|
+
get :private do
|
11
|
+
authenticate!
|
12
|
+
{ status: 'authenticated' }
|
13
|
+
end
|
14
|
+
|
15
|
+
get :private_info do
|
16
|
+
authenticate!
|
17
|
+
auth_info
|
18
|
+
end
|
19
|
+
|
20
|
+
params do
|
21
|
+
requires :email, type: String
|
22
|
+
requires :password, type: String
|
23
|
+
end
|
24
|
+
post :sign_in do
|
25
|
+
declared_params = declared(params)
|
26
|
+
|
27
|
+
# Perform logic to find user by posted e-mail
|
28
|
+
# - usually user = User.find_by_email(declared_params[:email])
|
29
|
+
#
|
30
|
+
# Here we just create a new instance of DummyUser
|
31
|
+
# the same way we created it in the test
|
32
|
+
user = DummyUser.new(1, Seraph::PasswordEncryptor.call('foobar12'))
|
33
|
+
|
34
|
+
token = sign_in(user, declared_params[:password])
|
35
|
+
status 200
|
36
|
+
{ jwt: token }
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
DummyUser = Struct.new(:id, :encrypted_password)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: seraph-grape
|
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
|
- Szymon Szeliga
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-06-
|
11
|
+
date: 2016-06-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: grape
|
@@ -38,20 +38,48 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: jwt
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.5'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.5'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rake
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
61
|
+
version: '11.2'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '11.2'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rack-test
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.6'
|
48
76
|
type: :development
|
49
77
|
prerelease: false
|
50
78
|
version_requirements: !ruby/object:Gem::Requirement
|
51
79
|
requirements:
|
52
80
|
- - "~>"
|
53
81
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
82
|
+
version: '0.6'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: rspec
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,7 +150,7 @@ dependencies:
|
|
122
150
|
- - "~>"
|
123
151
|
- !ruby/object:Gem::Version
|
124
152
|
version: '0.1'
|
125
|
-
description:
|
153
|
+
description: Integrate Seraph with your Grape API
|
126
154
|
email: szeliga.szymon@gmail.com
|
127
155
|
executables: []
|
128
156
|
extensions: []
|
@@ -134,18 +162,25 @@ files:
|
|
134
162
|
- ".rubocop.yml"
|
135
163
|
- ".ruby-version"
|
136
164
|
- ".travis.yml"
|
137
|
-
- ChangeLog.md
|
138
165
|
- Gemfile
|
139
166
|
- LICENSE.txt
|
140
167
|
- README.md
|
141
168
|
- Rakefile
|
142
169
|
- lib/seraph-grape.rb
|
143
170
|
- lib/seraph/grape.rb
|
171
|
+
- lib/seraph/grape/authenticator.rb
|
144
172
|
- lib/seraph/grape/configuration.rb
|
173
|
+
- lib/seraph/grape/helpers.rb
|
174
|
+
- lib/seraph/grape/jwt.rb
|
145
175
|
- lib/seraph/grape/version.rb
|
146
176
|
- seraph-grape.gemspec
|
177
|
+
- spec/seraph/grape/authenticator_spec.rb
|
178
|
+
- spec/seraph/grape/helpers_spec.rb
|
179
|
+
- spec/seraph/grape/jwt_spec.rb
|
147
180
|
- spec/seraph/grape_spec.rb
|
148
181
|
- spec/spec_helper.rb
|
182
|
+
- spec/support/dummy_api.rb
|
183
|
+
- spec/support/dummy_user.rb
|
149
184
|
homepage: https://rubygems.org/gems/seraph-grape
|
150
185
|
licenses:
|
151
186
|
- MIT
|
@@ -169,7 +204,12 @@ rubyforge_project:
|
|
169
204
|
rubygems_version: 2.5.1
|
170
205
|
signing_key:
|
171
206
|
specification_version: 4
|
172
|
-
summary:
|
207
|
+
summary: Seraph helpers for Grape
|
173
208
|
test_files:
|
209
|
+
- spec/seraph/grape/authenticator_spec.rb
|
210
|
+
- spec/seraph/grape/helpers_spec.rb
|
211
|
+
- spec/seraph/grape/jwt_spec.rb
|
174
212
|
- spec/seraph/grape_spec.rb
|
175
213
|
- spec/spec_helper.rb
|
214
|
+
- spec/support/dummy_api.rb
|
215
|
+
- spec/support/dummy_user.rb
|
data/ChangeLog.md
DELETED