keyless 1.0.1
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 +7 -0
- data/.editorconfig +30 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +41 -0
- data/.simplecov +3 -0
- data/.travis.yml +20 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile +8 -0
- data/LICENSE +21 -0
- data/README.md +279 -0
- data/Rakefile +8 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/doc/assets/project.svg +68 -0
- data/keyless.gemspec +36 -0
- data/lib/keyless.rb +47 -0
- data/lib/keyless/configuration.rb +82 -0
- data/lib/keyless/jwt.rb +109 -0
- data/lib/keyless/rsa_public_key.rb +128 -0
- data/lib/keyless/version.rb +5 -0
- metadata +226 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a742a4e768b944a62453aa8cc6d94e1afd138a5cd3d4059ed6f970ade717618d
|
4
|
+
data.tar.gz: 0c8e5392cc8443b303ddbfe67672ca54a56462041d84e95e4adcbddbb66bbc78
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 23f6a2b71ce821364c0c66aed4f585c80e74a4412bf204a08f95b265ab3790eacea59dcb1d5e5c8496bf1d06f2b0cf0d32c162f7a824f34a79a9bb0124d3b41a
|
7
|
+
data.tar.gz: fd1d10b84e31ca6eb624832a3c2426b2df9989fe9ceb14c1c5638d87c3673be66c0d6972c3f50bc0cbd6fb73dde677a01e82be8cecddf472c94a2ae644a79085
|
data/.editorconfig
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# http://editorconfig.org
|
2
|
+
root = true
|
3
|
+
|
4
|
+
[*]
|
5
|
+
indent_style = space
|
6
|
+
indent_size = 2
|
7
|
+
end_of_line = lf
|
8
|
+
charset = utf-8
|
9
|
+
trim_trailing_whitespace = true
|
10
|
+
insert_final_newline = true
|
11
|
+
|
12
|
+
[*.md]
|
13
|
+
trim_trailing_whitespace = true
|
14
|
+
|
15
|
+
[*.json]
|
16
|
+
indent_style = space
|
17
|
+
indent_size = 2
|
18
|
+
|
19
|
+
[*.yml]
|
20
|
+
indent_style = space
|
21
|
+
indent_size = 2
|
22
|
+
|
23
|
+
[Makefile]
|
24
|
+
trim_trailing_whitespace = true
|
25
|
+
indent_style = tab
|
26
|
+
indent_size = 4
|
27
|
+
|
28
|
+
[*.sh]
|
29
|
+
indent_style = space
|
30
|
+
indent_size = 2
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require: rubocop-rspec
|
2
|
+
|
3
|
+
Rails:
|
4
|
+
Enabled: true
|
5
|
+
|
6
|
+
Documentation:
|
7
|
+
Enabled: true
|
8
|
+
|
9
|
+
AllCops:
|
10
|
+
DisplayCopNames: true
|
11
|
+
TargetRubyVersion: 2.3
|
12
|
+
Exclude:
|
13
|
+
- db/schema.rb
|
14
|
+
- bin/**/*
|
15
|
+
- db/migrate/**/*
|
16
|
+
- vendor/cache/**/*
|
17
|
+
- vendor/bundle/**/*
|
18
|
+
- build/**/*
|
19
|
+
|
20
|
+
Metrics/BlockLength:
|
21
|
+
Exclude:
|
22
|
+
- Rakefile
|
23
|
+
- keyless.gemspec
|
24
|
+
- spec/**/*.rb
|
25
|
+
- '**/*.rake'
|
26
|
+
|
27
|
+
# Document all the things.
|
28
|
+
Style/DocumentationMethod:
|
29
|
+
Enabled: true
|
30
|
+
RequireForNonPublicMethods: true
|
31
|
+
|
32
|
+
# It's a deliberate idiom in RSpec.
|
33
|
+
# See: https://github.com/bbatsov/rubocop/issues/4222
|
34
|
+
Lint/AmbiguousBlockAssociation:
|
35
|
+
Exclude:
|
36
|
+
- "spec/**/*"
|
37
|
+
|
38
|
+
# Because +expect_any_instance_of().to have_received()+ is not
|
39
|
+
# supported with the +with(hash_including)+ matchers
|
40
|
+
RSpec/MessageSpies:
|
41
|
+
EnforcedStyle: receive
|
data/.simplecov
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
env:
|
2
|
+
global:
|
3
|
+
- CC_TEST_REPORTER_ID=ecb753423174dbd8e4aaf04fb62bf4ef9c2a54904ac49a33fdf2b908b3c5e5f3
|
4
|
+
|
5
|
+
sudo: false
|
6
|
+
language: ruby
|
7
|
+
rvm:
|
8
|
+
- 2.6
|
9
|
+
- 2.5
|
10
|
+
- 2.4
|
11
|
+
|
12
|
+
before_install: gem install bundler
|
13
|
+
|
14
|
+
before_script:
|
15
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
16
|
+
- chmod +x ./cc-test-reporter
|
17
|
+
- ./cc-test-reporter before-build
|
18
|
+
script: bundle exec rake
|
19
|
+
after_script:
|
20
|
+
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2017 HAUSGOLD | talocasa GmbH
|
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 all
|
13
|
+
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 THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,279 @@
|
|
1
|
+

|
2
|
+
|
3
|
+
[](https://travis-ci.com/hausgold/keyless)
|
4
|
+
[](https://badge.fury.io/rb/keyless)
|
5
|
+
[](https://www.rubydoc.info/gems/keyless)
|
6
|
+
|
7
|
+
This gem is dedicated to easily integrate a JWT authentication to your
|
8
|
+
ruby application. The real authentication
|
9
|
+
functionality must be provided by the user and this makes this gem highy
|
10
|
+
flexible on the JWT verification level.
|
11
|
+
|
12
|
+
- [Installation](#installation)
|
13
|
+
- [Configuration](#configuration)
|
14
|
+
- [Authenticator](#authenticator)
|
15
|
+
- [RSA public key helper](#rsa-public-key-helper)
|
16
|
+
- [RSA public key location (URL)](#rsa-public-key-location-url)
|
17
|
+
- [RSA public key caching](#rsa-public-key-caching)
|
18
|
+
- [RSA public key cache expiration](#rsa-public-key-cache-expiration)
|
19
|
+
- [JWT instance helper](#jwt-instance-helper)
|
20
|
+
- [Issuer verification](#issuer-verification)
|
21
|
+
- [Beholder (audience) verification](#beholder-audience-verification)
|
22
|
+
- [Custom JWT verification options](#custom-jwt-verification-options)
|
23
|
+
- [Custom JWT verification key](#custom-jwt-verification-key)
|
24
|
+
- [Full RSA256 example](#full-rsa256-example)
|
25
|
+
- [Development](#development)
|
26
|
+
- [Contributing](#contributing)
|
27
|
+
|
28
|
+
## Installation
|
29
|
+
|
30
|
+
Add this line to your application's Gemfile:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
gem 'keyless'
|
34
|
+
```
|
35
|
+
|
36
|
+
And then execute:
|
37
|
+
|
38
|
+
```bash
|
39
|
+
$ bundle
|
40
|
+
```
|
41
|
+
|
42
|
+
Or install it yourself as:
|
43
|
+
|
44
|
+
```bash
|
45
|
+
$ gem install keyless
|
46
|
+
```
|
47
|
+
|
48
|
+
## Configuration
|
49
|
+
|
50
|
+
This gem is quite customizable and flexible to fulfill your needs. You can make
|
51
|
+
use of some parts and leave other if you do not care about them. We are not
|
52
|
+
going to force the way how to verify JWT or work with them. Here comes a
|
53
|
+
overview of the configurations you can do.
|
54
|
+
|
55
|
+
### Authenticator
|
56
|
+
|
57
|
+
The authenticator function which must be defined by the user to verify the
|
58
|
+
given JSON Web Token. Here comes all your logic to lookup the related user on
|
59
|
+
your database, the token claim verification and/or the token cryptographic
|
60
|
+
signing. The function must return true or false to indicate the validity of the
|
61
|
+
token.
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
Keyless.configure do |conf|
|
65
|
+
conf.authenticator = proc do |token|
|
66
|
+
# Verify the token the way you like. (true, false)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
### RSA public key helper
|
72
|
+
|
73
|
+
We provide a straightforward solution to deal with the provision of RSA public
|
74
|
+
keys. Somethimes you want to distribute them by file to each machine and have
|
75
|
+
a local access, and somethimes you provide an endpoint on your identity
|
76
|
+
provider to fetch the RSA public key via HTTP/HTTPS. The `RsaPublicKey` class
|
77
|
+
helps you to fulfill this task easily.
|
78
|
+
|
79
|
+
**Heads up!** You can skip this if you do not care about RSA verification or
|
80
|
+
have your own mechanism.
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
# Get your public key, by using the global configuration
|
84
|
+
public_key = Keyless::RsaPublicKey.fetch
|
85
|
+
# => OpenSSL::PKey::RSA
|
86
|
+
|
87
|
+
# Using a local configuration
|
88
|
+
fetcher = Keyless::RsaPublicKey.instance
|
89
|
+
fetcher.url = 'https://your.identity.provider/rsa_public_key'
|
90
|
+
public_key = fetcher.fetch
|
91
|
+
# => OpenSSL::PKey::RSA
|
92
|
+
```
|
93
|
+
|
94
|
+
The following examples show you how to configure the
|
95
|
+
`Keyless::RsaPublicKey` class the global way. This is useful
|
96
|
+
for a shared initializer place.
|
97
|
+
|
98
|
+
#### RSA public key location (URL)
|
99
|
+
|
100
|
+
Whenever you want to use the `RsaPublicKey` class you configure the default URL
|
101
|
+
on the singleton instance, or use the gem configure method and set it up
|
102
|
+
accordingly. We allow the fetch of the public key from a remote server
|
103
|
+
(HTTP/HTTPS) or from a local file which is accessible by the ruby process.
|
104
|
+
Specify the URL or the local path here. Not specified by default.
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
Keyless.configure do |conf|
|
108
|
+
# Local file
|
109
|
+
conf.rsa_public_key_url = '/tmp/jwt_rsa.pub'
|
110
|
+
# Remote URL
|
111
|
+
conf.rsa_public_key_url = 'https://your.identity.provider/rsa_public_key'
|
112
|
+
end
|
113
|
+
```
|
114
|
+
|
115
|
+
#### RSA public key caching
|
116
|
+
|
117
|
+
You can configure the `RsaPublickey` class to enable/disable caching. For a
|
118
|
+
remote public key location it is handy to cache the result for some time to
|
119
|
+
keep the traffic low to the resource server. For a local file you can skip
|
120
|
+
this. Disabled by default.
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
Keyless.configure do |conf|
|
124
|
+
conf.rsa_public_key_caching = true
|
125
|
+
end
|
126
|
+
```
|
127
|
+
|
128
|
+
#### RSA public key cache expiration
|
129
|
+
|
130
|
+
When you make use of the cache of the `RsaPublicKey` class you can fine tune
|
131
|
+
the expiration time. The RSA public key from your identity
|
132
|
+
provider should not change this frequent, so a cache for at least one hour is
|
133
|
+
fine. You should not set it lower than one minute. Keep this setting in mind
|
134
|
+
when you change keys. Your infrastructure could be inoperable for this
|
135
|
+
configured time. One hour by default.
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
Keyless.configure do |conf|
|
139
|
+
conf.rsa_public_key_expiration = 1.hour
|
140
|
+
end
|
141
|
+
```
|
142
|
+
|
143
|
+
### JWT instance helper
|
144
|
+
|
145
|
+
We ship a little wrapper class to ease the validation of JSON Web Tokens with
|
146
|
+
the help of the great [ruby-jwt](https://github.com/jwt/ruby-jwt) library. This
|
147
|
+
wrapper class provides some helpers like `#access_token?`, `#refresh_token?` or
|
148
|
+
`#expires_at` which returns a ActiveSupport time-zoned representation of the
|
149
|
+
token expiration timestamp. It is initially opinionated to RSA verification,
|
150
|
+
but can be tuned to verify HMAC or ECDSA signed tokens. It integrated well with
|
151
|
+
the `RsaPublicKey` fetcher class. (by default)
|
152
|
+
|
153
|
+
**Heads up!** You can skip this if you have your own JWT verification mechanism.
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
# A raw JWT (no signing, payload: {test: true})
|
157
|
+
raw_token = 'eyJ0eXAiOiJKV1QifQ.eyJ0ZXN0Ijp0cnVlfQ.'
|
158
|
+
|
159
|
+
# Parse the raw token and create a instance of it
|
160
|
+
token = Keyless::Jwt.new(raw_token)
|
161
|
+
|
162
|
+
# Access the payload easily (recursive-open-struct)
|
163
|
+
token.payload.test
|
164
|
+
# => true
|
165
|
+
|
166
|
+
# Validate the token (we assume you configured the verification key, an/or
|
167
|
+
# you own custom JWT verification options here)
|
168
|
+
token.valid?
|
169
|
+
# => true
|
170
|
+
```
|
171
|
+
|
172
|
+
The following examples show you how to configure the
|
173
|
+
`Keyless::Jwt` class the global way. This is useful for a
|
174
|
+
shared initializer place.
|
175
|
+
|
176
|
+
#### Issuer verification
|
177
|
+
|
178
|
+
The JSON Web Token issuer which should be used for verification. When `nil` we
|
179
|
+
also turn off the verification by default. (See the default JWT options)
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
Keyless.configure do |conf|
|
183
|
+
conf.jwt_issuer = 'your-identity-provider'
|
184
|
+
end
|
185
|
+
```
|
186
|
+
|
187
|
+
#### Beholder (audience) verification
|
188
|
+
|
189
|
+
The resource server (namely the one which configures this right now)
|
190
|
+
which MUST be present on the JSON Web Token audience claim. When `nil` we
|
191
|
+
also turn off the verification by default. (See the default JWT options)
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
Keyless.configure do |conf|
|
195
|
+
conf.jwt_beholder = 'your-resource-server'
|
196
|
+
end
|
197
|
+
```
|
198
|
+
|
199
|
+
#### Custom JWT verification options
|
200
|
+
|
201
|
+
You can configure a different JSON Web Token verification option hash if your
|
202
|
+
algorithm differs or you want some extra/different options. Just watch out
|
203
|
+
that you have to pass a proc to this configuration property. On the
|
204
|
+
`Keyless::Jwt` class it has to be a simple hash. The default
|
205
|
+
is here the `RS256` algorithm with enabled expiration check, and issuer+audience
|
206
|
+
check when the `jwt_issuer` / `jwt_beholder` are configured accordingly.
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
Keyless.configure do |conf|
|
210
|
+
conf.jwt_options = proc do
|
211
|
+
# See: https://github.com/jwt/ruby-jwt
|
212
|
+
{ algorithm: 'HS256' }
|
213
|
+
end
|
214
|
+
end
|
215
|
+
```
|
216
|
+
|
217
|
+
#### Custom JWT verification key
|
218
|
+
|
219
|
+
You can configure your own verification key on the `Jwt` wrapper class. This
|
220
|
+
way you can pass your HMAC secret or your ECDSA public key to the JSON Web
|
221
|
+
Token validation method. Here you need to pass a proc, on the
|
222
|
+
`Keyless::Jwt` class it has to be a scalar value. By default
|
223
|
+
we use the `RsaPublicKey` class to retrieve the RSA public key.
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
Keyless.configure do |conf|
|
227
|
+
conf.jwt_verification_key = proc do
|
228
|
+
# Retrieve your verification key (RSA, ECDSA, HMAC secret)
|
229
|
+
# the way you like, and pass it back here.
|
230
|
+
end
|
231
|
+
end
|
232
|
+
```
|
233
|
+
|
234
|
+
### Full RSA256 example
|
235
|
+
|
236
|
+
Here comes a full example of the opinionated `RSA256` algorithm usage with a
|
237
|
+
remote RSA public key location, enabled caching and a full token payload
|
238
|
+
verification.
|
239
|
+
|
240
|
+
```ruby
|
241
|
+
# On an initializer ..
|
242
|
+
Keyless.configure do |conf|
|
243
|
+
# The remote RSA public key location and enabled caching to limit the
|
244
|
+
# traffic on the remote server.
|
245
|
+
conf.rsa_public_key_url = 'https://your.identity.provider/rsa_public_key'
|
246
|
+
conf.rsa_public_key_caching = true
|
247
|
+
conf.rsa_public_key_expiration = 10.minutes
|
248
|
+
|
249
|
+
# Configure the JWT wrapper.
|
250
|
+
conf.jwt_issuer = 'The Identity Provider'
|
251
|
+
conf.jwt_beholder = 'example-api'
|
252
|
+
|
253
|
+
# Custom verification logic.
|
254
|
+
conf.authenticator = proc do |token|
|
255
|
+
# Parse and instantiate a JWT verification instance
|
256
|
+
jwt = Keyless::Jwt.new(token)
|
257
|
+
|
258
|
+
# We just allow valid access tokens
|
259
|
+
jwt.access_token? && jwt.valid?
|
260
|
+
end
|
261
|
+
end
|
262
|
+
```
|
263
|
+
|
264
|
+
## Development
|
265
|
+
|
266
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
267
|
+
`bundle exec rake spec` to run the tests. You can also run `bin/console` for an
|
268
|
+
interactive prompt that will allow you to experiment.
|
269
|
+
|
270
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To
|
271
|
+
release a new version, update the version number in `version.rb`, and then run
|
272
|
+
`bundle exec rake release`, which will create a git tag for the version, push
|
273
|
+
git commits and tags, and push the `.gem` file to
|
274
|
+
[rubygems.org](https://rubygems.org).
|
275
|
+
|
276
|
+
## Contributing
|
277
|
+
|
278
|
+
Bug reports and pull requests are welcome on GitHub at
|
279
|
+
https://github.com/hausgold/keyless.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "keyless"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<svg
|
3
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
4
|
+
xmlns:cc="http://creativecommons.org/ns#"
|
5
|
+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
6
|
+
xmlns:svg="http://www.w3.org/2000/svg"
|
7
|
+
xmlns="http://www.w3.org/2000/svg"
|
8
|
+
version="1.1"
|
9
|
+
id="Ebene_1"
|
10
|
+
x="0px"
|
11
|
+
y="0px"
|
12
|
+
viewBox="0 0 800 200"
|
13
|
+
xml:space="preserve"
|
14
|
+
width="800"
|
15
|
+
height="200"><metadata
|
16
|
+
id="metadata33"><rdf:RDF><cc:Work
|
17
|
+
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
18
|
+
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
19
|
+
id="defs31" />
|
20
|
+
<style
|
21
|
+
type="text/css"
|
22
|
+
id="style2">
|
23
|
+
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#E73E11;}
|
24
|
+
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#0371B9;}
|
25
|
+
.st2{fill:#132E48;}
|
26
|
+
.st3{font-family:'OpenSans-Bold';}
|
27
|
+
.st4{font-size:29.5168px;}
|
28
|
+
.st5{fill-rule:evenodd;clip-rule:evenodd;fill:none;}
|
29
|
+
.st6{opacity:0.5;fill:#132E48;}
|
30
|
+
.st7{font-family:'OpenSans';}
|
31
|
+
.st8{font-size:12px;}
|
32
|
+
</style>
|
33
|
+
<g
|
34
|
+
transform="translate(0,1.53584)"
|
35
|
+
id="g828"><g
|
36
|
+
transform="translate(35.93985,35.66416)"
|
37
|
+
id="g8">
|
38
|
+
<path
|
39
|
+
style="clip-rule:evenodd;fill:#e73e11;fill-rule:evenodd"
|
40
|
+
id="path4"
|
41
|
+
d="m -0.1,124.4 c 0,0 33.7,-123.2 66.7,-123.2 12.8,0 26.9,21.9 38.8,47.2 -23.6,27.9 -66.6,59.7 -94,76 -7.1,0 -11.5,0 -11.5,0 z"
|
42
|
+
class="st0" />
|
43
|
+
<path
|
44
|
+
style="clip-rule:evenodd;fill:#0371b9;fill-rule:evenodd"
|
45
|
+
id="path6"
|
46
|
+
d="m 88.1,101.8 c 13.5,-10.4 18.4,-16.2 27.1,-25.4 10,25.7 16.7,48 16.7,48 0,0 -41.4,0 -78,0 14.6,-7.9 18.7,-10.7 34.2,-22.6 z"
|
47
|
+
class="st1" />
|
48
|
+
</g><text
|
49
|
+
y="106.40316"
|
50
|
+
x="192.43155"
|
51
|
+
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:29.51733398px;font-family:'Open Sans', sans-serif;-inkscape-font-specification:'OpenSans-Bold, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#132e48"
|
52
|
+
id="text10"
|
53
|
+
class="st2 st3 st4">keyless</text>
|
54
|
+
<rect
|
55
|
+
style="clip-rule:evenodd;fill:none;fill-rule:evenodd"
|
56
|
+
id="rect12"
|
57
|
+
height="24"
|
58
|
+
width="314.5"
|
59
|
+
class="st5"
|
60
|
+
y="118.06416"
|
61
|
+
x="194.23985" /><text
|
62
|
+
y="127.22146"
|
63
|
+
x="194.21715"
|
64
|
+
style="font-size:12px;font-family:'Open Sans', sans-serif;opacity:0.5;fill:#132e48;-inkscape-font-specification:'Open Sans, sans-serif, Normal';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;text-anchor:start;text-align:start;writing-mode:lr;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;"
|
65
|
+
id="text14"
|
66
|
+
class="st6 st7 st8">A reusable JWT authentication concern</text>
|
67
|
+
</g>
|
68
|
+
</svg>
|
data/keyless.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'keyless/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'keyless'
|
9
|
+
spec.version = Keyless::VERSION
|
10
|
+
spec.authors = ['Hermann Mayer', 'Christopher Mühl', 'Marcus Geißler']
|
11
|
+
spec.email = ['hermann.mayer92@gmail.com', 'christopher@padarom.xyz', 'mg@hausgold.de']
|
12
|
+
|
13
|
+
spec.summary = 'A reusable JWT authentication helper'
|
14
|
+
spec.description = 'A reusable JWT authentication helper'
|
15
|
+
spec.homepage = 'https://github.com/hausgold/keyless'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = 'exe'
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ['lib']
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '>= 1.16', '< 3'
|
25
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
26
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
27
|
+
spec.add_development_dependency 'simplecov', '~> 0.15'
|
28
|
+
spec.add_development_dependency 'timecop', '~> 0.9.1'
|
29
|
+
spec.add_development_dependency 'vcr', '~> 3.0'
|
30
|
+
spec.add_development_dependency 'webmock', '~> 3.1'
|
31
|
+
|
32
|
+
spec.add_runtime_dependency 'activesupport', '>= 3.2.0'
|
33
|
+
spec.add_runtime_dependency 'httparty'
|
34
|
+
spec.add_runtime_dependency 'jwt', '~> 2.1'
|
35
|
+
spec.add_runtime_dependency 'recursive-open-struct', '~> 1.0'
|
36
|
+
end
|
data/lib/keyless.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/concern'
|
5
|
+
require 'active_support/configurable'
|
6
|
+
require 'active_support/cache'
|
7
|
+
require 'active_support/core_ext/hash'
|
8
|
+
require 'active_support/time'
|
9
|
+
require 'active_support/time_with_zone'
|
10
|
+
|
11
|
+
require 'jwt'
|
12
|
+
|
13
|
+
require 'keyless/version'
|
14
|
+
require 'keyless/configuration'
|
15
|
+
require 'keyless/jwt'
|
16
|
+
require 'keyless/rsa_public_key'
|
17
|
+
|
18
|
+
# The JWT authentication concern.
|
19
|
+
module Keyless
|
20
|
+
extend ActiveSupport::Concern
|
21
|
+
|
22
|
+
class << self
|
23
|
+
attr_writer :configuration
|
24
|
+
end
|
25
|
+
|
26
|
+
# Retrieve the current configuration object.
|
27
|
+
#
|
28
|
+
# @return [Configuration]
|
29
|
+
def self.configuration
|
30
|
+
@configuration ||= Configuration.new
|
31
|
+
end
|
32
|
+
|
33
|
+
# Configure the concern by providing a block which takes
|
34
|
+
# care of this task. Example:
|
35
|
+
#
|
36
|
+
# Keyless.configure do |conf|
|
37
|
+
# # conf.xyz = [..]
|
38
|
+
# end
|
39
|
+
def self.configure
|
40
|
+
yield(configuration)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Reset the current configuration with the default one.
|
44
|
+
def self.reset_configuration!
|
45
|
+
self.configuration = Configuration.new
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Keyless
|
4
|
+
# The configuration for the JWT authentication concern.
|
5
|
+
class Configuration
|
6
|
+
include ActiveSupport::Configurable
|
7
|
+
|
8
|
+
# The authenticator function which must be defined by the user to
|
9
|
+
# verify the given JSON Web Token. Here comes all your logic to lookup
|
10
|
+
# the related user on your database, the token claim verification
|
11
|
+
# and/or the token cryptographic signing. The function must return true
|
12
|
+
# or false to indicate the validity of the token.
|
13
|
+
#
|
14
|
+
# Keyless.configure do |conf|
|
15
|
+
# conf.authenticator = proc do |token|
|
16
|
+
# # Verify the token the way you like.
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
config_accessor(:authenticator) { proc { false } }
|
20
|
+
|
21
|
+
# Whenever you want to use the {RsaPublicKey} class you configure the
|
22
|
+
# default URL on the singleton instance, or use the gem configure
|
23
|
+
# method and set it up accordingly. We allow the fetch of the public
|
24
|
+
# key from a remote server (HTTP/HTTPS) or from a local file which is
|
25
|
+
# accessible by the ruby process. Specify the URL or the local path
|
26
|
+
# here.
|
27
|
+
config_accessor(:rsa_public_key_url) { nil }
|
28
|
+
|
29
|
+
# You can preconfigure the {RsaPublickey} class to enable/disable
|
30
|
+
# caching. For a remote public key location it is handy to cache the
|
31
|
+
# result for some time to keep the traffic low to this resource server.
|
32
|
+
# For a local file you can skip this.
|
33
|
+
config_accessor(:rsa_public_key_caching) { false }
|
34
|
+
|
35
|
+
# When you make use of the caching of the {RsaPublicKey} class you can
|
36
|
+
# fine tune the expiration time of this cache. The RSA public key from
|
37
|
+
# your identity provider should not change this frequent, so a cache
|
38
|
+
# for at least one hour is fine. You should not set it lower than one
|
39
|
+
# minute. Keep this setting in mind when you change keys. Your
|
40
|
+
# infrastructure could be inoperable for this configured time.
|
41
|
+
config_accessor(:rsa_public_key_expiration) { 1.hour }
|
42
|
+
|
43
|
+
# The JSON Web Token isser which should be used for verification.
|
44
|
+
config_accessor(:jwt_issuer) { nil }
|
45
|
+
|
46
|
+
# The resource server (namely the one which configures this right now)
|
47
|
+
# which MUST be present on the JSON Web Token audience claim.
|
48
|
+
config_accessor(:jwt_beholder) { nil }
|
49
|
+
|
50
|
+
# You can configure a different JSON Web Token verification option hash
|
51
|
+
# if your algorithm differs or you want some extra/different options.
|
52
|
+
# Just watch out that you have to pass a proc to this configuration
|
53
|
+
# property. On the {Keyless::Jwt} class it has to be
|
54
|
+
# a simple hash. The default is here the RS256 algorithm with enabled
|
55
|
+
# expiration check, and issuer+audience check when the
|
56
|
+
# {jwt_issuer}/{jwt_beholder} are configured accordingly.
|
57
|
+
config_accessor(:jwt_options) do
|
58
|
+
proc do
|
59
|
+
conf = ::Keyless.configuration
|
60
|
+
{ algorithm: 'RS256',
|
61
|
+
exp_leeway: 30.seconds.to_i,
|
62
|
+
iss: conf.jwt_issuer,
|
63
|
+
verify_iss: !conf.jwt_issuer.nil?,
|
64
|
+
aud: conf.jwt_beholder,
|
65
|
+
verify_aud: !conf.jwt_beholder.nil?,
|
66
|
+
# @TODO: https://github.com/jwt/ruby-jwt/issues/247
|
67
|
+
verify_iat: false }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# You can configure your own verification key on the Jwt wrapper class.
|
72
|
+
# This way you can pass your HMAC secret or your ECDSA public key to
|
73
|
+
# the JSON Web Token validation method. Here you need to pass a proc,
|
74
|
+
# on the {Keyless::Jwt} class it has to be a scalar
|
75
|
+
# value. By default we use the
|
76
|
+
# {Keyless::RsaPublicKey} class to retrieve the RSA
|
77
|
+
# public key.
|
78
|
+
config_accessor(:jwt_verification_key) do
|
79
|
+
proc { RsaPublicKey.instance.fetch }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/keyless/jwt.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'recursive-open-struct'
|
4
|
+
|
5
|
+
module Keyless
|
6
|
+
# A easy to use model for verification of JSON Web Tokens. This is just a
|
7
|
+
# wrapper class for the excellent ruby-jwt gem. It's completely up to you
|
8
|
+
# to use it. But be aware, its a bit optinionated by default.
|
9
|
+
class Jwt
|
10
|
+
# All the following JWT verification issues lead to a failed validation.
|
11
|
+
RESCUE_JWT_EXCEPTIONS = [
|
12
|
+
::JWT::DecodeError,
|
13
|
+
::JWT::VerificationError,
|
14
|
+
::JWT::ExpiredSignature,
|
15
|
+
::JWT::IncorrectAlgorithm,
|
16
|
+
::JWT::ImmatureSignature,
|
17
|
+
::JWT::InvalidIssuerError,
|
18
|
+
::JWT::InvalidIatError,
|
19
|
+
::JWT::InvalidAudError,
|
20
|
+
::JWT::InvalidSubError,
|
21
|
+
::JWT::InvalidJtiError,
|
22
|
+
::JWT::InvalidPayload
|
23
|
+
].freeze
|
24
|
+
|
25
|
+
# :reek:Attribute because its fine to be extern-modifiable at these
|
26
|
+
# instances
|
27
|
+
attr_reader :payload, :token
|
28
|
+
attr_writer :verification_key, :jwt_options
|
29
|
+
attr_accessor :issuer, :beholder
|
30
|
+
|
31
|
+
# Setup a new JWT instance. You have to pass the raw JSON Web Token to
|
32
|
+
# the initializer. Example:
|
33
|
+
#
|
34
|
+
# Jwt.new('j.w.t')
|
35
|
+
# # => <Jwt>
|
36
|
+
#
|
37
|
+
# @return [Jwt]
|
38
|
+
def initialize(token)
|
39
|
+
parsed_payload = JWT.decode(token, nil, false).first.symbolize_keys
|
40
|
+
@token = token
|
41
|
+
@payload = RecursiveOpenStruct.new(parsed_payload)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Checks if the payload says this is a refresh token.
|
45
|
+
#
|
46
|
+
# @return [Boolean] Whenever this is a access token
|
47
|
+
def access_token?
|
48
|
+
payload.typ == 'access'
|
49
|
+
end
|
50
|
+
|
51
|
+
# Checks if the payload says this is a refresh token.
|
52
|
+
#
|
53
|
+
# @return [Boolean] Whenever this is a refresh token
|
54
|
+
def refresh_token?
|
55
|
+
payload.typ == 'refresh'
|
56
|
+
end
|
57
|
+
|
58
|
+
# Retrives the expiration date from the payload when set.
|
59
|
+
#
|
60
|
+
# @return [nil|ActiveSupport::TimeWithZone] The expiration date
|
61
|
+
def expires_at
|
62
|
+
exp = payload.exp
|
63
|
+
return nil unless exp
|
64
|
+
|
65
|
+
Time.zone.at(exp)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Deliver the public key for verification by default. This uses the
|
69
|
+
# {RsaPublicKey} class, but you can configure the verification key the
|
70
|
+
# way you like. (Especially for different algorithms, like HMAC or
|
71
|
+
# ECDSA) Just make use of the same named setter.
|
72
|
+
#
|
73
|
+
# @return [OpenSSL::PKey::RSA|Mixed] The verification key
|
74
|
+
def verification_key
|
75
|
+
unless @verification_key
|
76
|
+
conf = ::Keyless.configuration
|
77
|
+
return conf.jwt_verification_key.call
|
78
|
+
end
|
79
|
+
@verification_key
|
80
|
+
end
|
81
|
+
|
82
|
+
# This getter passes back the default JWT verification option hash
|
83
|
+
# which is optinionated. You can change this the way you like by
|
84
|
+
# configuring your options with the help of the same named setter.
|
85
|
+
#
|
86
|
+
# @return [Hash] The JWT verification options hash
|
87
|
+
def jwt_options
|
88
|
+
unless @jwt_options
|
89
|
+
conf = ::Keyless.configuration
|
90
|
+
return conf.jwt_options.call
|
91
|
+
end
|
92
|
+
@jwt_options
|
93
|
+
end
|
94
|
+
|
95
|
+
# Verify the current token by our hard and strict rules. Whenever the
|
96
|
+
# token was not parsed from a string, we encode the current state to a
|
97
|
+
# JWT string representation and check this.
|
98
|
+
#
|
99
|
+
# @return [Boolean] Whenever the token is valid or not
|
100
|
+
#
|
101
|
+
# :reek:NilCheck because we have to check the token
|
102
|
+
# origin and react on it
|
103
|
+
def valid?
|
104
|
+
JWT.decode(token, verification_key, true, jwt_options) && true
|
105
|
+
rescue *RESCUE_JWT_EXCEPTIONS
|
106
|
+
false
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
require 'openssl'
|
5
|
+
require 'httparty'
|
6
|
+
|
7
|
+
module Keyless
|
8
|
+
# A common purpose RSA public key fetching/caching helper. With the help
|
9
|
+
# of this class you are able to retrieve the RSA public key from a remote
|
10
|
+
# server or a local file. This is naturally only useful if you care about
|
11
|
+
# JSON Web Token which are signed by the RSA algorithm.
|
12
|
+
class RsaPublicKey
|
13
|
+
# A internal exception handling for failed fetch attempts.
|
14
|
+
class FetchError < StandardError; end
|
15
|
+
|
16
|
+
include Singleton
|
17
|
+
|
18
|
+
# Setup all the getters and setters.
|
19
|
+
attr_accessor :cache
|
20
|
+
attr_writer :url, :expiration, :caching
|
21
|
+
|
22
|
+
# Setup the instance.
|
23
|
+
def initialize
|
24
|
+
@expiration = 1.hour
|
25
|
+
@cache = ActiveSupport::Cache::MemoryStore.new
|
26
|
+
end
|
27
|
+
|
28
|
+
# Just a simple shortcut class method to access the fetch method
|
29
|
+
# without specifying the singleton instance.
|
30
|
+
#
|
31
|
+
# @return [OpenSSL::PKey::RSA]
|
32
|
+
def self.fetch
|
33
|
+
instance.fetch
|
34
|
+
end
|
35
|
+
|
36
|
+
# Configure the single instance. This is just a wrapper (like tap)
|
37
|
+
# to the instance itself.
|
38
|
+
def configure
|
39
|
+
yield(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Fetch the public key with the help of the configuration. You can
|
43
|
+
# configure the public key location (local file, remote (HTTP/HTTPS)
|
44
|
+
# file), whenever we should cache and how long to cache.
|
45
|
+
#
|
46
|
+
# @return [OpenSSL::PKey::RSA]
|
47
|
+
def fetch
|
48
|
+
encoded_key = if cache?
|
49
|
+
cache.fetch('encoded_key', expires_in: expiration) do
|
50
|
+
fetch_encoded_key
|
51
|
+
end
|
52
|
+
else
|
53
|
+
fetch_encoded_key
|
54
|
+
end
|
55
|
+
|
56
|
+
OpenSSL::PKey::RSA.new(encoded_key)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Fetch the encoded (DER, or PEM) public key from a remote or local
|
60
|
+
# location.
|
61
|
+
#
|
62
|
+
# @return [String] The encoded public key
|
63
|
+
def fetch_encoded_key
|
64
|
+
raise ArgumentError, 'No URL for RsaPublicKey configured' unless url
|
65
|
+
|
66
|
+
if remote?
|
67
|
+
res = HTTParty.get(url)
|
68
|
+
raise FetchError, res.inspect unless (200..299).include? res.code
|
69
|
+
res.body
|
70
|
+
else
|
71
|
+
File.read(url)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# A helper for the caching configuration.
|
76
|
+
#
|
77
|
+
# @return [Boolean]
|
78
|
+
def cache?
|
79
|
+
caching && true
|
80
|
+
end
|
81
|
+
|
82
|
+
# A helper to determine if the configured URL is on a remote server or
|
83
|
+
# it is local on the filesystem. Whenever the configured URL specifies
|
84
|
+
# the HTTP/HTTPS protocol, we assume it is remote.
|
85
|
+
#
|
86
|
+
# @return [Boolean]
|
87
|
+
def remote?
|
88
|
+
!(url =~ /^https?/).nil?
|
89
|
+
end
|
90
|
+
|
91
|
+
# This getter passes back the default RSA public key. You can change
|
92
|
+
# this the way you like by configuring your URL with the help of the
|
93
|
+
# same named setter.
|
94
|
+
#
|
95
|
+
# @return [String] The configured public key location
|
96
|
+
def url
|
97
|
+
unless @url
|
98
|
+
conf = ::Keyless.configuration
|
99
|
+
return conf.rsa_public_key_url
|
100
|
+
end
|
101
|
+
@url
|
102
|
+
end
|
103
|
+
|
104
|
+
# This getter passes back the default public key cache expiration time.
|
105
|
+
# You can change this time with the help of the same named setter.
|
106
|
+
#
|
107
|
+
# @return [Integer] The configured cache expiration time
|
108
|
+
def expiration
|
109
|
+
unless @expiration
|
110
|
+
conf = ::Keyless.configuration
|
111
|
+
return conf.rsa_public_key_expiration
|
112
|
+
end
|
113
|
+
@expiration
|
114
|
+
end
|
115
|
+
|
116
|
+
# This getter passes back the caching flag. You can change this flag
|
117
|
+
# with the help of the same named setter.
|
118
|
+
#
|
119
|
+
# @return [Boolean] Whenever we should cache or not
|
120
|
+
def caching
|
121
|
+
unless @caching
|
122
|
+
conf = ::Keyless.configuration
|
123
|
+
return conf.rsa_public_key_caching
|
124
|
+
end
|
125
|
+
@caching
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
metadata
ADDED
@@ -0,0 +1,226 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: keyless
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Hermann Mayer
|
8
|
+
- Christopher Mühl
|
9
|
+
- Marcus Geißler
|
10
|
+
autorequire:
|
11
|
+
bindir: exe
|
12
|
+
cert_chain: []
|
13
|
+
date: 2020-09-01 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: bundler
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ">="
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.16'
|
22
|
+
- - "<"
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '3'
|
25
|
+
type: :development
|
26
|
+
prerelease: false
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '1.16'
|
32
|
+
- - "<"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '3'
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rake
|
37
|
+
requirement: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '10.0'
|
42
|
+
type: :development
|
43
|
+
prerelease: false
|
44
|
+
version_requirements: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '10.0'
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: rspec
|
51
|
+
requirement: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '3.0'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '3.0'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: simplecov
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0.15'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0.15'
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: timecop
|
79
|
+
requirement: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 0.9.1
|
84
|
+
type: :development
|
85
|
+
prerelease: false
|
86
|
+
version_requirements: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 0.9.1
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: vcr
|
93
|
+
requirement: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '3.0'
|
98
|
+
type: :development
|
99
|
+
prerelease: false
|
100
|
+
version_requirements: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - "~>"
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '3.0'
|
105
|
+
- !ruby/object:Gem::Dependency
|
106
|
+
name: webmock
|
107
|
+
requirement: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '3.1'
|
112
|
+
type: :development
|
113
|
+
prerelease: false
|
114
|
+
version_requirements: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - "~>"
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '3.1'
|
119
|
+
- !ruby/object:Gem::Dependency
|
120
|
+
name: activesupport
|
121
|
+
requirement: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 3.2.0
|
126
|
+
type: :runtime
|
127
|
+
prerelease: false
|
128
|
+
version_requirements: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: 3.2.0
|
133
|
+
- !ruby/object:Gem::Dependency
|
134
|
+
name: httparty
|
135
|
+
requirement: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
type: :runtime
|
141
|
+
prerelease: false
|
142
|
+
version_requirements: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
- !ruby/object:Gem::Dependency
|
148
|
+
name: jwt
|
149
|
+
requirement: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - "~>"
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '2.1'
|
154
|
+
type: :runtime
|
155
|
+
prerelease: false
|
156
|
+
version_requirements: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - "~>"
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '2.1'
|
161
|
+
- !ruby/object:Gem::Dependency
|
162
|
+
name: recursive-open-struct
|
163
|
+
requirement: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - "~>"
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: '1.0'
|
168
|
+
type: :runtime
|
169
|
+
prerelease: false
|
170
|
+
version_requirements: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - "~>"
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '1.0'
|
175
|
+
description: A reusable JWT authentication helper
|
176
|
+
email:
|
177
|
+
- hermann.mayer92@gmail.com
|
178
|
+
- christopher@padarom.xyz
|
179
|
+
- mg@hausgold.de
|
180
|
+
executables: []
|
181
|
+
extensions: []
|
182
|
+
extra_rdoc_files: []
|
183
|
+
files:
|
184
|
+
- ".editorconfig"
|
185
|
+
- ".gitignore"
|
186
|
+
- ".rspec"
|
187
|
+
- ".rubocop.yml"
|
188
|
+
- ".simplecov"
|
189
|
+
- ".travis.yml"
|
190
|
+
- CHANGELOG.md
|
191
|
+
- Gemfile
|
192
|
+
- LICENSE
|
193
|
+
- README.md
|
194
|
+
- Rakefile
|
195
|
+
- bin/console
|
196
|
+
- bin/setup
|
197
|
+
- doc/assets/project.svg
|
198
|
+
- keyless.gemspec
|
199
|
+
- lib/keyless.rb
|
200
|
+
- lib/keyless/configuration.rb
|
201
|
+
- lib/keyless/jwt.rb
|
202
|
+
- lib/keyless/rsa_public_key.rb
|
203
|
+
- lib/keyless/version.rb
|
204
|
+
homepage: https://github.com/hausgold/keyless
|
205
|
+
licenses: []
|
206
|
+
metadata: {}
|
207
|
+
post_install_message:
|
208
|
+
rdoc_options: []
|
209
|
+
require_paths:
|
210
|
+
- lib
|
211
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - ">="
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '0'
|
216
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
217
|
+
requirements:
|
218
|
+
- - ">="
|
219
|
+
- !ruby/object:Gem::Version
|
220
|
+
version: '0'
|
221
|
+
requirements: []
|
222
|
+
rubygems_version: 3.0.8
|
223
|
+
signing_key:
|
224
|
+
specification_version: 4
|
225
|
+
summary: A reusable JWT authentication helper
|
226
|
+
test_files: []
|